Everhart, Glenn From: Jay Soffian [jay@CIMEDIA.COM] Sent: Friday, July 17, 1998 1:04 AM To: BUGTRAQ@NETSPACE.ORG Subject: Verity/Search'97 Security Problems Sorry about the extra traffic. I probably fired off the veritywrap.c sooner than I should have. There was a bug or two in it that I've squashed. This is the last version I'll send. We spoke with Verity today and they are supposed to have patched binaries out RSN. j. -- Jay Soffian UNIX Systems Administrator 404.572.1941 Cox Interactive Media /* * Author: Jay Soffian * * Idea: s97_cgi doesn't check ResultTemplate for a valid path, so it * is possible to read arbitrary files using something like * ResultTemplate=../../../../../../etc/passwd * This script decodes the input (from either a GET or a POST), * cleans up ResultTemplate, then execs s97_cgi * * History: o fixed bug with cp not being correct after realloc in * makequery() * o fixed handling of isindex queries * * usage: copy s97_cgi, s97r_cgi, etc to s97_cgi.orig * compile this program and install it as s97_cgi, s97r_cgi, etc * in the same directory as s97_cgi.orig * DON"T FORGET TO RESTRICT ACCESS TO THE *.orig BINARIES, OR THIS * WRAPPER DOESN"T DO MUCH GOOD. Use (apache) or equiv. * * 16 July 98 */ #include #include #include #include #include #define verity_path_pre "/path/to/dir/with/verity/s97_cgi/" /* notice trailing slash */ #define verity_path_post ".orig" int num_params = 0; typedef struct { char *name; char *val; } param; char * keywords = NULL; char method; #define MAX_PARAMS 1000 #define GET 'G' #define POST 'P' param params[MAX_PARAMS]; void die(char *logmsg, char *usermsg) { if (!usermsg) usermsg = "internal error"; printf("Content-type: text/html\n\n"); printf("\n" "Error\n" "\n" "

Error

\n" "Error: %s

\n" "Please try your request again.

\n" "\n", usermsg); fprintf(stderr,logmsg); exit(0); } char * escape(char *s) { register unsigned char *a, *b; char *buffer; b = s; a = buffer = (char *)malloc((strlen(s)*3)+1); if (!buffer) die ("malloc() failed", NULL); while (*b) { if (*b <= '\x20' || *b >= '\x7f' || strchr("\"#%;<>?{}|\\^~`[]&", *b)) { *a++ = '%'; sprintf(a,"%0X",*b); a+=2; b++; } else { *a++ = *b++; } } *a = '\0'; return buffer; } void unescape(char *s) { register unsigned char *a, *b; if (!*s) return; for ((a = b = s);(*a = *b); ++a, ++b) { switch (*b) { case '%': if (*(b+1) && *(b+2)) { *b = toupper(*b); b++; *a = *b >= 'A' ? (((*b & 0xdf) - 'A') +10) : *b - '0'; *a *= 16; b++; *a += *b >= 'A' ? (((*b & 0xdf) - 'A') +10) : *b - '0'; } break; case '+': *a = ' '; break; } } } void cgiinit(void) { int content_length = 0, i = 0; char *query = NULL, *cp = NULL, *request_method = getenv("REQUEST_METHOD"); if (!request_method) die("No REQUEST_METHOD", NULL); if (!strcmp(request_method,"POST")) { method = POST; if(getenv("CONTENT_TYPE") && strcmp(getenv("CONTENT_TYPE"),"application/x-www-form-urlencoded")) die("CONTENT_TYPE not application/x-www-form-urlencoded", "CONTENT_TYPE must be 'application/x-www-form-urlencoded'."); if (getenv("CONTENT_LENGTH")) content_length = atoi(getenv("CONTENT_LENGTH")); else die("No CONTENT_LENGTH", NULL); query = (char *)malloc(sizeof(char) * content_length + 1); if (query == NULL) die ("malloc() failed",NULL); if (fread(query,sizeof(char),content_length,stdin) != content_length) die("Ran out of input while processing request", NULL); query[content_length] = '\0'; } else if (!strcmp(request_method,"GET")) { method = GET; if (getenv("QUERY_STRING")) query = strdup(getenv("QUERY_STRING")); else die("No QUERY_STRING",NULL); if (!query) die ("malloc() failed",NULL); } else { die ("Method not GET nor POST", "Method must be 'GET' or 'POST'."); } /* input is in query. let's parse it. */ if (strchr(query,'=')) { /* name = value pairs */ params[i].name = strtok(query, "&"); while ((++i < MAX_PARAMS) && (params[i].name = strtok(NULL, "&"))); /* tokenize */ num_params = i; for(i=0; i < num_params; i++) { if ((cp = strchr(params[i].name, '='))) { *cp = '\0'; params[i].val = cp+1; } else { params[i].val = ""; } unescape(params[i].name); unescape(params[i].val); } } else { /* isindex */ unescape(query); keywords = query; } } void fixpath (register char * a) { register char *b = a; if (!*a) return; if (*a == '/') b++; while (*b) if (*b == '.' && *(b+1) == '.' && *(b+2) == '/') b+=3; else *a++ = *b++; *a = '\0'; } char * makequery(void) { char * buf, *cp, *name, *val; int i, offset, tot_len = 0, len, size = 1024; cp = buf = (char *)malloc(size); if (!buf) die ("malloc() failed", NULL); for (i=0; i size) { while (tot_len > size) size *=2; offset = cp - buf; buf = realloc(buf, size); if (!buf) die ("realloc() failed", NULL); cp = buf + offset; } sprintf(cp,"%s=%s&",name,val); cp+=len; tot_len +=len; free(name); free(val); } *(cp-1) = '\0'; return buf; } int main (int argc, char **argv) { int size, i, fd[2], pid; char *query, *path, ssize[128], *buf; path = strrchr(argv[0],'/'); if (!path) path = argv[0]; else path++; buf = malloc(sizeof(verity_path_pre) + sizeof(verity_path_post) + strlen(path) + 1); if (!buf) die("malloc() failed", NULL); sprintf(buf,"%s%s%s",verity_path_pre, path, verity_path_post); path = buf; cgiinit(); for (i=0; i