CoreHTTP <= 0.5.3.1 Remote Command Execution

Overview

CoreHTTP server fails to properly sanitize input before calling popen() and allows an attacker using a standard web browser to execute arbitrary commands.

NOTE: depending on the script and directory permissions, the attacker may not be able to view output.

Analysis

During code review / debugging of CoreHTTP, a look at http.c source file revealed:



/* escape the url for " and \ since we use it in popen */
for (i = 0; i < PATHSIZE; i++) {
    if (url[i] == '\0') break;
    else if (url[i] == '\\' || url[i] == '\"' || url[i] == '\'') {
        find = url + i;
        strcpy(temp, find);
        *find = '\\';
        *(find+1) = '\0';
        strcat(url, temp);
        i++;
    }
}


In the above code, only " and \ are escaped, allowing one to specify |`& and any other special formatting.

The URL then gets broken into two parts:

  • url (which in this case is a script)
  • args (which contains our 'evil' buffer)

There is a caveat, however:



if (c == 0) { /* TODO our dirlist perl script takes the path
                  of the dir as the arg. the
                  way we do cgi
                  right now is scipt.pl?arg turns into
                  commandprompt> ./script.pl arg. obviously
                  when urlencode is implemented correctly this
                  must be changed. */
    strcpy(args, url);
    strcpy(url, DIRLIST);
    break;
    }


In this, we can see that DIRLIST overwrites the value of url and url overwrites the value of args - so for simple directory listing this vulnerability becomes a bit more difficult to exploit (depending on directory name, the system could still be vulnerable).

Finally, here's the call to popen:



. . .
} else if (cmd[0] != '\0') { /* if its dynamic content */
    pipe(pipefd); /* make pipe then fork */
    c = fork();
    if (c > 0) { /* original, keep going */
        close(pipefd[1]); /* no need to write */
        sprocket->fd = pipefd[0];
        SetNonBlock(sprocket->fd);
    } else if (c == 0) { /* child, popen */
        close(pipefd[0]); /* no need to read */
        pipetoprog = popen(cmd, "r");
. . .


And there you have it. Simply download coreHTTP for yourself, build, enable CGI, touch foo.pl and then send it a request for /foo.pl%60command%26%60 which will set url to /foo.pl and args to `command&` and call popen. Voila!

Last modified: 2011-03-02, 21:46

Copyright © 2011 me