HexBrain

Valve · Report #763403

[GoldSrc] RCE via malformed BSP file

Validstatus
highseverity
$450bounty
24upvotes
19 Aug 2020disclosed

Summary

#Description RCE can be achieved via a malformed BSP file due to the lack of length validation when copying data from the BSP file into a stack based buffer.

#POC

  1. Place the attached BSP {F666628} in the maps directory of the chosen GoldSrc game (czero/maps, cstrike/maps, tfc/maps, etc..)
  2. Launch the game and bring up the console with ~
  3. Type in map de_RCE and press enter
  4. View calc pop

#Vulnerable Code Within the following function, COM_FileBase copies data from pszWadFile into wadName without any length validation which leads to a buffer overflow.

qboolean TEX_InitFromWad(char *path)
{
	char *pszWadFile;
	FileHandle_t texfile;
	char szTmpPath[1024];
	char wadName[260];
	char wadPath[260];
	wadinfo_t header;

	Q_strncpy(szTmpPath, path, 1022);
	szTmpPath[1022] = 0;
	if (!Q_strchr(szTmpPath, ';'))
		Q_strcat(szTmpPath, ";");
	for (pszWadFile = strtok(szTmpPath, ";"); pszWadFile; pszWadFile = strtok(NULL, ";"))
	{
		ForwardSlashes(pszWadFile);
		COM_FileBase(pszWadFile, wadName);//Vulnerable Function
		Q_snprintf(wadPath, 0x100u, "%s", wadName);
		COM_DefaultExtension(wadPath, ".wad");

		if (Q_strstr(wadName, "pldecal") || Q_strstr(wadName, "tempdecal"))
			continue;

		texfile = FS_Open(wadPath, "rb");
		texfiles[nTexFiles++] = texfile;
		if (!texfile)
			Sys_Error("%s: couldn't open %s\n", __func__, wadPath);

		Con_DPrintf("Using WAD File: %s\n", wadPath);
		SafeRead(texfile, &header, 12);
		if (Q_strncmp(header.identification, "WAD2", 4) && Q_strncmp(header.identification, "WAD3", 4))
			Sys_Error("%s: %s isn't a wadfile", __func__, wadPath);

		header.numlumps = LittleLong(header.numlumps);
		header.infotableofs = LittleLong(header.infotableofs);
		FS_Seek(texfile, header.infotableofs, FILESYSTEM_SEEK_HEAD);
		lumpinfo = (texlumpinfo_t *)Mem_Realloc(lumpinfo, sizeof(texlumpinfo_t) * (header.numlumps + nTexLumps));

		for (int i = 0; i < header.numlumps; i++, nTexLumps++)
		{
			SafeRead(texfile, &lumpinfo[nTexLumps], sizeof(lumpinfo_t));
			CleanupName(lumpinfo[nTexLumps].lump.name, lumpinfo[nTexLumps].lump.name);
			lumpinfo[nTexLumps].lump.filepos = LittleLong(lumpinfo[nTexLumps].lump.filepos);
			lumpinfo[nTexLumps].lump.disksize = LittleLong(lumpinfo[nTexLumps].lump.disksize);
			lumpinfo[nTexLumps].iTexFile = nTexFiles - 1;
		}

	}
	qsort(lumpinfo, nTexLumps, sizeof(texlumpinfo_t), lump_sorter);
	return 1;
}

Impact

RCE can be used to execute any arbitrary code that an attacker could want to execute on any victim of choice.

How it was found

The write-up for this report has not been downloaded yet, or the program disclosed it without one. Read it on HackerOne.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original