前置知识
Mojo & Services 简介
chromium mojo 快速入门
Mojo docs
Intro to Mojo & Services
参考文章
本文主要参考 Plaid CTF 2020 mojo Writeup
环境搭建
题目环境
给了 docker
环境,所以直接启 docker
即可。
安装 docker
:
sudo snap install docker
运行 run.sh
脚本:
./run.sh
运行 chrome
:
./chrome --disable-gpu --remote-debugging-port=1338 --enable-blink-features=MojoJS,MojoJSTest url
调试环境
这里单独启一个 web
服务:
python3 -m http.server 8000
调试脚本:
# gdbinit
# 读取符号
file ./chrome
# 设置启动参数
set args --disable-gpu --remote-debugging-port=1338 --user-data-dir=./userdata --enable-blink-features=MojoJS url
# 设置执行fork后继续调试父进程
set follow-fork-mode parent
然后 gdb
调试即可:
gdb -x gdbinit
题目分析
附件分析
题目新定义了一个 PlaidStore
接口:
module blink.mojom;
// This interface provides a data store
interface PlaidStore {
// Stores data in the data store
StoreData(string key, array<uint8> data);
// Gets data from the data store
GetData(string key, uint32 count) => (array<uint8> data);
};
该接口定义了两个方法 StoreData
、GetData
分别用于向 data store
中存储数据和获取数据。
然后在浏览器端实现 PlaidStore
接口:
namespace content {
class RenderFrameHost;
class PlaidStoreImpl : public blink::mojom::PlaidStore {
public:
explicit PlaidStoreImpl(RenderFrameHost *render_frame_host);
static void Create(
RenderFrameHost* render_frame_host,
mojo::PendingReceiver<blink::mojom::PlaidStore> receiver);
~PlaidStoreImpl() override;
// PlaidStore overrides:
void StoreData(
const std::string &key,
const std::vector<uint8_t> &data) override;
void GetData(
const std::string &key,
uint32_t count,
GetDataCallback callback) override;
private:
RenderFrameHost* render_frame_host_;
std::map<std::string, std::vector<uint8_t> > data_store_;
};
}
可以看到这里存在两个私有变量其中一个是 data_store_
,这个好理解,其就是用来存储数据的;这里的 render_frame_host_
是神马东西呢?
render
进程中的每一个 frame
都在 browser
进程中对应一个 RenderFrameHost
,很多由浏览器提供的 mojo
接口就是通过 RenderFrameHoset
获取的。在 RenderFrameHost
初始化阶段,会在 BinderMap
中填充所有公开的 mojo
接口:
@@ -660,6 +662,10 @@ void PopulateFrameBinders(RenderFrameHostImpl* host,
map->Add<blink::mojom::SerialService>(base::BindRepeating(
&RenderFrameHostImpl::BindSerialService, base::Unretained(host)));
#endif // !defined(OS_ANDROID)
+
+ map->Add<blink::mojom::PlaidStore>(
+ base::BindRepeating(&RenderFrameHostImpl::CreatePlaidStore,
+ base::Unretained(host)));
}
当一个 render frame
请求该接口时,在 BinderMap
中关联的回调函数 RenderFrameHostImpl::CreatePlaidStore
就会被调用,其定义如下:
void RenderFrameHostImpl::CreatePlaidStore(
mojo::PendingReceiver<blink::mojom::PlaidStore> receiver) {
PlaidStoreImpl::Create(this, std::move(receiver));
}
其直接调用了 PlaidStoreImpl::Create
函数:
// static
void PlaidStoreImpl::Create(
RenderFrameHost *render_frame_host,
mojo::PendingReceiver<blink::mojom::PlaidStore> receiver) {
mojo::MakeSelfOwnedReceiver(std::make_unique<PlaidStoreImpl>(render_frame_host),
std::move(receiver));
}
通过该函数,一个 PlaidStoreImpl
就被创建,并且该 PendingReceiver
与一个 SelfOwnedReceiver
绑定。
漏洞分析
该题存在两个漏洞,分别是 OOB
与 UAF
,接下来直接分别讲解。
OOB
来分析下存取数据的操作:
void PlaidStoreImpl::StoreData(
const std::string &key,
const std::vector<uint8_t> &data) {
if (!render_frame_host_->IsRenderFrameLive()) {
return;
}
data_store_[key] = data;
}
void PlaidStoreImpl::GetData(
const std::string &key,
uint32_t count,
GetDataCallback callback) {
if (!render_frame_host_->IsRenderFrameLive()) {
std::move(callback).Run({
});
return;
}