chrome sandbox escape case study and plaidctf2020 mojo writeup
字数 1525 2025-08-20 18:18:24
Chrome沙箱逃逸案例研究与PlaidCTF 2020 Mojo漏洞分析
Mojo基础
Mojo简介
Mojo是Chromium项目中的进程间通信(IPC)系统,用于在不同进程间传递消息和接口调用。
核心概念
-
Message Pipe:一对端点(endpoints),每个端点保存一个传入消息队列,写入一端的消息会传送到另一端,实现双向通信。
-
Mojom文件:描述一组接口(interfaces),代表强类型的消息集合。
-
Remote与Receiver:
- Remote:用于发送接口描述的消息
- Receiver:用于接收接口的消息并分发给具体实现
-
接口调用流程:
- 客户端通过Remote发送消息
- 服务端通过Receiver接收并处理消息
- 需要响应时,Receiver会通过Remote返回响应
Mojo接口开发流程
定义新接口
- 创建.mojom文件:
module example.mojom;
interface PingResponder {
Ping() => (int32 random);
};
- 添加构建规则:
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [ "ping_responder.mojom" ]
}
创建和使用接口管道
- 创建管道:
mojo::Remote<example::mojom::PingResponder> ping_responder;
mojo::PendingReceiver<example::mojom::PingResponder> receiver =
ping_responder.BindNewPipeAndPassReceiver();
- 发送消息:
ping_responder->Ping(base::BindOnce(&OnPong));
- 发送PendingReceiver到Browser进程:
RenderFrame* my_frame = GetMyFrame();
my_frame->GetBrowserInterfaceBroker().GetInterface(std::move(receiver));
实现接口
class PingResponderImpl : example::mojom::PingResponder {
public:
explicit PingResponderImpl(mojo::PendingReceiver<example::mojom::PingResponder> receiver)
: receiver_(this, std::move(receiver)) {}
void Ping(PingCallback callback) override {
std::move(callback).Run(4);
}
private:
mojo::Receiver<example::mojom::PingResponder> receiver_;
};
Mojo绑定类型
1. Self-owned Receivers
自动管理生命周期的接收器:
mojo::MakeSelfOwnedReceiver(std::make_unique<LoggerImpl>(),
logger.BindNewPipeAndPassReceiver());
2. Receiver Sets
用于多个客户端共享同一个实现实例:
class LogManager : public system::mojom::LoggerProvider,
public system::mojom::Logger {
public:
void GetLogger(mojo::PendingReceiver<Logger> receiver) override {
logger_receivers_.Add(this, std::move(receiver));
}
private:
mojo::ReceiverSet<system::mojom::Logger> logger_receivers_;
};
3. Remote Sets
维护一组Remote,如事件观察者:
class TableImpl : public db::mojom::Table {
public:
void AddListener(mojo::PendingRemote<db::mojom::TableListener> listener) {
listeners_.Add(std::move(listener));
}
private:
mojo::RemoteSet<db::mojom::Table> listeners_;
};
4. Associated Interfaces
允许在单个消息管道上多路复用多个接口:
interface Foo {
PassBarRemote(pending_associated_remote<Bar> bar);
PassBarReceiver(pending_associated_receiver<Bar> bar);
};
JavaScript绑定API
基本用法
<script src="URL/to/mojo_bindings.js"></script>
<script src="URL/to/echo.mojom.js"></script>
<script>
var echoPtr = new test.echo.mojom.EchoPtr();
var echoRequest = mojo.makeRequest(echoPtr);
</script>
接口实现示例
function EchoImpl() {}
EchoImpl.prototype.echoInteger = function(value) {
return Promise.resolve({result: value});
};
var echoServicePtr = new test.echo.mojom.EchoPtr();
var echoServiceRequest = mojo.makeRequest(echoServicePtr);
var echoServiceBinding = new mojo.Binding(test.echo.mojom.Echo,
new EchoImpl(),
echoServiceRequest);
漏洞案例分析
案例1: RenderFrameHost生命周期问题
漏洞原理:
InstalledAppProviderImpl通过MakeSelfOwnedReceiver与消息管道绑定生命周期- 但保存了
RenderFrameHost的原始指针,未绑定生命周期 - 当iframe被移除,
RenderFrameHost被释放但InstalledAppProviderImpl仍存在 - 后续调用
FilterInstalledApps时使用已释放的RenderFrameHost
关键代码:
void InstalledAppProviderImpl::FilterInstalledApps(
std::vector<blink::mojom::RelatedApplicationPtr> related_apps,
const GURL& manifest_url,
FilterInstalledAppsCallback callback) {
if (render_frame_host_->GetProcess()->GetBrowserContext()->IsOffTheRecord()) {
// UAF可能发生在这里
}
}
案例2: PlaidCTF 2020 Mojo漏洞
漏洞组合:
-
UAF漏洞:
- 类似案例1,
PlaidStoreImpl与消息管道绑定但保存RenderFrameHost原始指针 - 移除iframe后仍可通过保留的Remote调用方法
- 类似案例1,
-
OOB读取漏洞:
void PlaidStoreImpl::GetData(
const std::string &key,
uint32_t count,
GetDataCallback callback) {
// ...
std::vector<uint8_t> result(it->second.begin(), it->second.begin() + count);
// 无长度检查导致越界读取
}
利用技术
-
信息泄露:
- 通过OOB读取泄露虚表地址和chrome基地址
- 计算ROP gadget位置
-
堆喷技术:
- 分配大量与
RenderFrameHost大小相同的vector - 伪造虚表和ROP链
- 分配大量与
-
利用流程:
- 创建子iframe并获取其
RenderFrameHost地址 - 将Mojo接口Remote传递给父frame
- 移除子iframe释放
RenderFrameHost - 通过堆喷占位伪造对象
- 触发虚函数调用实现控制流劫持
- 创建子iframe并获取其
防御措施
-
正确管理生命周期:
- 使用
WebContentsObserver等机制跟踪对象生命周期 - 避免保存原始指针
- 使用
-
输入验证:
- 对所有输入参数进行严格验证
- 特别是数组索引和长度参数
-
安全编码实践:
- 使用智能指针管理资源
- 最小化接口暴露
- 遵循Chromium安全编码指南
总结
Mojo作为Chromium的核心IPC机制,其安全性直接影响整个浏览器的安全性。开发者需要特别注意:
- 接口生命周期的正确管理
- 跨进程传递对象的所有权转移
- 输入参数的严格验证
- 避免在接口实现中保存原始指针
通过分析这些案例,可以更好地理解Mojo接口的安全风险和防御方法。