Package name: CoreHTTP server Version: 0.5.3.1 and below (as long as cgi support is enabled) Software URL: http://corehttp.sourceforge.net/ Exploit: http://aconole.brad-x.com/programs/corehttp_cgienabled.rb Issue: 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. Further Discussion: 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, only " and \ are escaped, allowing one to specify |`& and any other special formatting. The URL then gets broken into 2 parts: - url (which in this case is a script) - args (which contains our 'evil' buffer) There is a caveat though: 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"); /* fread should be non-blocking for this to exit fast when parent proc closes pipe */ while ((i = fread(temp, 1, BUFSIZE, pipetoprog)) != 0 && write(pipefd[1], temp, i) > 0); pclose(pipetoprog); close(pipefd[1]); exit(EXIT_SUCCESS); /* exit after done */ } else { /* failed */ RemoveSprock(sprocket, &FIRSTSPROCK); return NULL; } 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!