948d0399a8d2e1f294fb04c82c8d2fbec735707a max Tue Aug 11 07:33:13 2026 -0700 edw: fix heap overflow (edwWebXSendFile) and submit command injection (edwLib) edwWebXSendFile tokenValid()/getFullFileName() used strcat onto exact-sized cloneString buffers, overrunning the heap; tokenValid() does this before the auth token is checked, so it is reachable without valid credentials. Build the strings in right-sized buffers instead. edwAddSubmitJob() interpolated a user-supplied submission URL into a shell command later run by edwRunDaemon; a single quote broke out of the single-quote context and injected commands. Reject quotes/newlines in url and userEmail and single-quote both values in the command. refs #38055, refs #38060 diff --git src/hg/encode3/encodeDataWarehouse/edwWebXSendFile/edwWebXSendFile.c src/hg/encode3/encodeDataWarehouse/edwWebXSendFile/edwWebXSendFile.c index 25428ca5050..deb0b37ce99 100644 --- src/hg/encode3/encodeDataWarehouse/edwWebXSendFile/edwWebXSendFile.c +++ src/hg/encode3/encodeDataWarehouse/edwWebXSendFile/edwWebXSendFile.c @@ -57,60 +57,69 @@ /* Return the value of user (email) in the query string */ { return cloneString(cgiUsualString("user", "")); } char *getFullFileName(char * licensePlate) /* Return a complete path given dir and basename of file associated with this licenses plate */ { struct sqlConnection *conn = edwConnect(); char query[512]; char *fileName; char *fullFileName; sqlSafef(query, sizeof(query), "select edwFileName from vf where licensePlate = '%s'", licensePlate); fileName = sqlQuickString(conn, query); -fullFileName = strcat(edwDataRoot(), fileName); +/* Do not strcat onto edwDataRoot()'s exact-sized cloneString buffer, which overruns it; + * build a right-sized string instead. refs #38055 */ +char *dataRoot = edwDataRoot(); +fullFileName = catTwoStrings(dataRoot, fileName); +freeMem(dataRoot); return fullFileName; } char *getBaseName(char *fullFileName) /* Return basename part of the full file name */ { return basename(fullFileName); } void xSendFile(char *baseName, char *fullFileName) /* send the file out using xsendfile */ { printf("Content-Description: File Transfer\n"); printf("Content-Disposition: attachment; filename=\"%s\"\n", baseName); printf("Content-Transfer-Encoding: binary\n"); printf("Expires: 0\n"); printf("Cache-Control: no-store, no-cache, must-revalidate\n"); printf("Pragma: no-cache\n"); printf("Content-Length: %lld\n", (long long)fileSize(fullFileName)); printf("X-Sendfile: %s\n\n", fullFileName); } boolean tokenValid(char *accession, char *date, char *token, char *user) /* Check the token to ensure that the query string was created by * encodedcc without any modification */ { -char *hStr=strcat(strcat(date,accession),user); -char *digest; -digest=hmacSha1(edwKey(), hStr); +/* Build the HMAC input in a right-sized buffer; do not strcat onto the callers' + * exact-sized cgi string buffers, which overruns them (and runs before this token + * check, so it is reachable without valid credentials). refs #38055 */ +int hLen = strlen(date) + strlen(accession) + strlen(user) + 1; +char *hStr = needMem(hLen); +safef(hStr, hLen, "%s%s%s", date, accession, user); +char *digest = hmacSha1(edwKey(), hStr); +freez(&hStr); if (sameString(digest, token)) return TRUE; return FALSE; } int main(int argc, char *argv[]) /* Process command line. */ { boolean isFromWeb = cgiIsOnWeb(); if (!isFromWeb && !cgiSpoof(&argc, argv)) usage(); char *lp = getLicensePlate(); char *tk = getToken(); char *dt = getDate(); char *user = getUserMail();