为支持老大failwest的新书《0day安全:软件漏洞分析技术》的发行,响应看雪在漏洞分析领域的雄起,上期献上一篇《Microsoft TIFF图像文件处理栈溢出漏洞(MS07-055)》,本期继续贡献我在《黑客防线》上发表过得漏洞案例实战分析文章。希望通过这两篇文章,使大家对文件格式漏洞有一个初步的认识。


漏洞概述

  2007年3月30日,ph4nt0m和milw0rm先后公布了今年目前为止危害性最大的windows漏洞——user32.dll的ANI文件处理漏洞。我写这篇文章已是漏洞爆出的第5天了,事实上,30号公布的漏洞,30号晚上利用这个漏洞的网马生成器已经在网上散播了,而且微软迟迟没有发布相关补丁。这给利用这个漏洞的病毒、蠕虫、木马、恶意软件等,一个很难得的机会。漏洞就像一把钥匙,打开了它们侵入互联网的大门。
  虽然eEye推出了一款非官方的补丁,可以拦截一部分远程攻击、网络木马,但是4月1日,milw0rm上又公布了一个能够绕过eEye补丁的exploit。看来又是一场互联网血雨腥风的到来!
  
漏洞分析

  既然这个漏洞是和ANI文件有关的,那么我们在分析漏洞之前,首先需要对ANI文件格式有一定的了解。关于ANI文件格式的详细说明请读者参阅光盘中的附件。
ANI(APPlicedon Startins Hour Glass)文件是MS Windows的动画光标文件,可以作为鼠标指针,其文件扩展名为“.ani”。ANI文件由“块”(chunk)构成。它一般由五部分构成:标志区、文字说明区、信息区、时间控制区和数据区,即RIFF—ACON,LIST—INFO,anih,rate,LIST—fram。ANI文件在播放时所形成的动画效果,其实就是一张张的光标或图标图像按一定的顺序绘制到屏幕上,并保留指定的时间(见时间控制区说明)依序循环显示的结果。一个ANI文件的开头12个字节中,头4个字节为RIFF,后4个字节为ACON,中间4个字节是该ANI文件的长度(字节数)。有了RIFF和ACON,就可断定该文件是一个ANI文件,也就是说,ACON是一个动画光标文件的标志。
在一个ANI文件中,必须有的块标识有以下几个:
  RIFF—多媒体文件识别码
  ACON—ANI文件识别码
  anih—ANI文件信息区识别码
  LIST—LIST列表形式(窗体形式fccType=“fram”)
  icon—icon识别码
在一个ANI文件中,还可能有以下几个块标识中的一个或多个:
  INAM—ANI文件标题区识别码
  IART—ANI文件说明信息区识别码
  rate—ANI文件时间控制数据区识别码
  seq —ANI文件图像显示帧顺序控制区识别码
这些块之间的逻辑层次关系可以如下表示:
"RIFF" {Length of File}
    "ACON"
        "LIST" {Length of List}
            "INAM" {Length of Title} {Data}
            "IART" {Length of Author} {Data}
        "fram"
            "icon" {Length of Icon} {Data}      ; 1st in list
            ...
            "icon" {Length of Icon} {Data}      ; Last in list  (1 to cFrames)
    "anih" {Length of ANI header (36 bytes)} {Data}   ; (see ANI Header TypeDef )
    "rate" {Length of rate block} {Data}      ; ea. rate is a long (length is 1 to cSteps)
    "seq " {Length of sequence block} {Data} ; ea. seq is a long (length is 1 to cSteps)
-END-
  以上仅是对ANI文件格式的结论性描述,要完全明白这些块的作用和意义,以及块与块之间的逻辑关系,确实需要下一番功夫。另外仅研读我给的资料是完全不够的,一定会有理解起来模糊的地方,因此还需要动手做实验,自己跟踪user32.dll对ANI文件的处理过程,从实验中得到结论。这样做有两个好处,一是可以更加深刻的理解漏洞的根源;二是由此可以启发新漏洞的挖掘思想。
  如果你对ANI文件格式已有一定了解,那我们就可以开始分析造成最终user32.dll栈溢出的这个畸形.ani文件了,为了理解,我把这个ANI文件(exp.ani)用十六进制的方式打开,并示意如下:
 名称:  1.JPG
查看次数: 527
文件大小:  143.3 KB
  这是一个精心构造的1k大小的.ani文件(在XP SP2全补丁下测试通过),其各个部分解释如下表所示:
偏移地址  内容  含义  块
0000-0003h  “RIFF”  多媒体文件识别码  RIFF
0004-0007h  0000 0400h  文件大小(1k)  
0008-000Bh  “ACON”  ANI文件识别码ACON  
000C-000Fh  “anih”  anih识别码(ckID),信息区开始的标志  anih
0010-0013h  0000 0024h  anih块的大小(ckSize,36个字节)  
0014-0037h    anih块内容  
0038-003Bh  “LIST”  LIST识别码,数据区开始的标志  LIST
003C-003Fh  0000 0003  LIST块的大小(3个字节)  
0040-0043h    LIST块内容  
0044-0047h  “LIST”  LIST识别码,数据区开始的标志  LIST
0048-004Bh  0000 0003  LIST块的大小(3个字节)  
004C-004Fh    LIST块内容  
0050-0053h  “anih”  anih识别码(ckID),信息区开始的标志  anih
0054-0057h  0000 03A8h  anih块的大小(ckSize,936个字节)  
0058-03F0h    anih块内容  
  为什么这个文件能使user32.dll栈溢出?据我分析,是因为user32.dll对这个.ani文件中的第二个anih块处理时,未对这个块的长度进行检查,最终导致栈溢出,控制了程序的EIP。通常来说任何一个正常的anih块的长度应该是36个字节,为什么这样说呢?因为anih块内容是有自己的数据结构的,用一个结构体来表示:
typedef DWORD JIF,*PJIF;
typedef struct tagANIHEADER{
  DWORD  cbSizeof;      //数据块大小,应该是36字节
  DWORD  cFrames;      //ANI文件保存的图象桢数
  DWORD  cSteps;      //完成一次动画过程要显示的图象数
  DWORD  cx;      //图象宽度
  DWORD  cy;      //图象高度
  DWORD  cBitCount;      //颜色位数
  DWORD  cPlanes;      //颜色位面数
  JIF  jifRate;      //JIF速率
  DWORD  fl;      //AF_ICON/AF_SEQUENCE设置标记
}ANIHEADER,*PANIHEADER;
  可见,这个anih块内容是一个9个双字的结构体,所以说它的长度应该是9×4=36字节。然而这个畸形的.ani文件中第二个anih块内容的长度偏偏不为36字节,而是936个字节,若程序中把这个块的内容复制到栈中,你说这能不溢出吗?
  下面我们用ollyDBG跟踪一下这个溢出过程,看看根本原因是不是如上所说。
    //user32.dll中的ReadChunk(x,x,x)函数
77D53AC7   8BFF             MOV EDI,EDI
77D53AC9   55               PUSH EBP
77D53ACA   8BEC             MOV EBP,ESP
77D53ACC   56               PUSH ESI
77D53ACD   8B75 08          MOV ESI,DWORD PTR SS:[EBP+8]
77D53AD0   57               PUSH EDI
77D53AD1   8B7D 0C          MOV EDI,DWORD PTR SS:[EBP+C]
77D53AD4   FF77 04          PUSH DWORD PTR DS:[EDI+4]   ;anih块内容的长度
77D53AD7   FF75 10          PUSH DWORD PTR SS:[EBP+10]
77D53ADA   56               PUSH ESI
77D53ADB   E8 7AFFFFFF      CALL USER32.77D53A5A  ;调用ReadFileCopy函数
77D53AE0   85C0             TEST EAX,EAX

//user32.dll中的ReadFileCopy(x,x,x)函数
77D53A5A   8BFF             MOV EDI,EDI
77D53A5C   55               PUSH EBP
77D53A5D   8BEC             MOV EBP,ESP
77D53A5F   8B45 08          MOV EAX,DWORD PTR SS:[EBP+8]
77D53A62   8B55 10          MOV EDX,DWORD PTR SS:[EBP+10]
77D53A65   56               PUSH ESI
77D53A66   8B70 04          MOV ESI,DWORD PTR DS:[EAX+4]  ;让ESI指向堆中的anih块内容
77D53A69   8D0C16           LEA ECX,DWORD PTR DS:[ESI+EDX]
77D53A6C   3BCE             CMP ECX,ESI
77D53A6E   72 28            JB SHORT USER32.77D53A98
77D53A70   3BCA             CMP ECX,EDX
77D53A72   72 24            JB SHORT USER32.77D53A98
77D53A74   3B48 08          CMP ECX,DWORD PTR DS:[EAX+8]
77D53A77   77 1F            JA SHORT USER32.77D53A98
77D53A79   53               PUSH EBX
77D53A7A   57               PUSH EDI
77D53A7B   8B7D 0C          MOV EDI,DWORD PTR SS:[EBP+C]  ;让EDI指向栈帧中的一个内存单元
77D53A7E   8BCA             MOV ECX,EDX
77D53A80   8BD9             MOV EBX,ECX
77D53A82   C1E9 02          SHR ECX,2  ; 让ECX为.ani文件中第二个anih块的块内容大小的1/4
;一次复制一个双字,所以下面REP指令的循环次数应该为字节数的四分之一
77D53A85   F3:A5            REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
77D53A87   8BCB             MOV ECX,EBX
77D53A89   83E1 03          AND ECX,3
77D53A8C   F3:A4            REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
77D53A8E   0150 04          ADD DWORD PTR DS:[EAX+4],EDX
77D53A91   33C0             XOR EAX,EAX
77D53A93   5F               POP EDI
77D53A94   40               INC EAX
77D53A95   5B               POP EBX
77D53A96   EB 02            JMP SHORT USER32.77D53A9A
77D53A98   33C0             XOR EAX,EAX
77D53A9A   5E               POP ESI
77D53A9B   5D               POP EBP
77D53A9C   C2 0C00          RETN 0C
  上面是对溢出过程的动态跟踪分析,如果用IDA进行静态分析同样也会得到上面的提到的漏洞原因,这里不再赘述!

漏洞利用

  提到利用,大家就笑了!想想多少东西在使用user32.dll啊?!
首当其冲的就是IE了,IE6,IE7已被测试通过,全部中招,也就是说写个网络木马是很容易了,只要有人敢上你的网站,那说都不说了,想干啥就干啥喽!
  其次,Windows的Explorer在打开这个.ani文件所在的目录时也会调用user32.dll中的LoadAniIcon函数,从而触发溢出。因此电子邮件、QQ、ftp、U盘等都可以用来传播利用这个漏洞的病毒、蠕虫、木马、恶意软件。
  另外,还可以利用一些使用user32.dll中LoadAniIcon函数的软件,用这些软件打开这些畸形.ani文件,同样也会中招。譬如,WinHex、IrfanView、Firework、Photoshop等。
  本文主要演示网络木马生成和一次成功的攻击过程。
  网上已经流传了很多木马生成器,使用很简单,输入网页木马远程存放地址和木马程序存放地址即可。
    
  我使用了我的IP地址(202.117.7.209)作为测试,然后启动web服务(IIS5.1),把生成的那三个文件(index.htm,z1.jpg,z2.jpg),还有木马程序(a.exe)放到web跟目录下。
  然后把链接(http://202.117.7.209)给几个朋友,发现他们全部中招了,哈哈,好在我没有下毒手,那个木马程序仅仅是弹出个框框而已!
 名称:  2.JPG
查看次数: 521
文件大小:  91.8 KB

漏洞防范

  这个漏洞危害这么大,不防不行啊,广大网民岂不是要遭殃啊!因为我写这篇文章时还没有微软的官方补丁,首推的防范措施是下载eEye的非官方补丁,补丁已放在光盘附件中。
  另外如果你的C盘是NTFS格式的,那么还可以使用如下措施:
1.  “开始”菜单“运行”里输入"gpedit.msc"
2.  然后在“本地计算机”策略 => 用户配置 => 管理模板 => 系统 => 停止命令提示符   
设置为“启用”
3.  把“他停用命令提示符脚本处理吗”选为“是”,再按确定!
4.  C:\Documents and Settings\xxxxxx\Local Settings目录的权限设置为不能运行! xxxxxx是你的用户名

总结

  这个漏洞的根本原因是,user32.dll中的ReadChunk()函数未对.ani文件中的anih块内容长度进行检查,就直接调用ReadFilePtrCopy()函数把堆中的anih块内容复制到栈帧中。
无论利用这个漏洞的病毒、蠕虫、木马、恶意软件、网马对互联网的冲击有多大,我们都有理由相信,互联网及我们的PC能顶过这07年的第一次危机!
  值得一提的是,如果您是搞漏洞挖掘方面的,通过这次uesr32.dll爆出的漏洞,您是否体会到漏洞的真正含义呢?不光是那些一般水平程序员写的程序容易出问题,爆漏洞;事实上,高级程序员也会有考虑不周的时候。因此漏洞是一个很微妙的东西,不是说编程经验丰富、实现能力强的程序员写的程序就不会有漏洞,任何程序员只要编程时考虑不周,都有可能爆出漏洞!本漏洞就是一个很好的例子。