记录分析过的一些典型的chrome sandbox ndays

Man Yue Mo yyds

其他一些chrome sandbox资料:

https://theori.io/research/escaping-chrome-sandbox/

https://securitylab.github.com/research/one_day_short_of_a_fullchain_sbx/

chrome attack surface

Google chrome mojo文档:

chrome mojo js api

chrome C++ Bindings API

gdb chrome调试

1
2
# 设置执行fork后继续调试父进程
set follow-fork-mode parent

mojom接口定义的struct中类似

1
array<PaymentShippingOption> shipping_options;

对应的js结构体名称是

1
xx.shippingOptions

P0_1735:

https://bugs.chromium.org/p/project-zero/issues/detail?id=1735

PaymentRequest::Init初始化了一个spec智能指针

spec_ = std::make_unique<PaymentRequestSpec>(xx)

PaymentRequestDialogView::ShowInitialPaymentSheet()里创建PaymentSheetViewController类型的智能指针时调用其构造函数,将unique_ptr spec_转化成纯指针(raw pointer)。由于PaymentRequest::Init可以被多次调用,如果其在调用时spec_已经初始化,unique_ptr spec_会被释放掉;但是PaymentSheetViewController构造函数被调用时的raw pointer spec_并没有释放,在PaymentSheetViewController访问spec_会导致uaf。

即,例如如下代码,如果trigger()可以被多次调用,_b::_p会uaf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "iostream"
#include "memory"

void put(std::string str){std::cout << str << std::endl;}

class a
{
public:
a()
{
std::cout << "a constructor" << std::endl;
}
~a(){std::cout << "a deconstructor" << std::endl;}

void (*func)(std::string str);
};

class b : public a
{
public:
b(a *_p):_p(_p)
{
std::cout << "b constructor" << std::endl;
};

~b(){std::cout << "b decontructor" << std::endl;}

a *_p;
};

std::unique_ptr<class a> _a;
std::unique_ptr<class b> _b;

void trigger()
{
_a = std::make_unique<a>();
}

int main()
{
trigger();
_b = std::make_unique<b>(_a.get());
std::cout << _a.get() << std::endl << _b->_p << std::endl;
_b->_p->func = put;
trigger(); //[***]
_b->_p->func("uaf");
std::cout << _a.get() << std::endl << _b->_p << std::endl;
}

在[***]处,_b->_p会uaf

image-20210426132216344

或者直接使用asan编译可以看到uaf的asan信息。

1
2
3
4
g++ xx.cc -g -fsanitize=address -o xx

=================================================================
==2451510==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010 at pc 0x555d4107396b bp 0x7fffac937230 sp 0x7fffac937220

P0_1755:

https://bugs.chromium.org/p/project-zero/issues/detail?id=1755

base::Unretained会将智能指针转化为纯指针,这样会丢失智能指针lifetime的一些特性。chrome README中的解释

file_system.mojom中FileWriter::Write接口定义如下,其中第二个参数是一个用js可控的blob对象,需要注意这里可以控制并在js里释放掉FileWriterImpl实例。

1
2
3
4
5
//src/third_party/blink/public/mojom/filesystem/file_system.mojom
interface FileWriter {
Write(uint64 position, Blob blob) => (mojo_base.mojom.FileError result,
uint64 bytes_written);
}
1
PS:Blob = binary data object that can be referenced via URL

FileWriter::Write接口对应实现代码如下,漏洞的根源在于GetBlobDataFromBlobPtr的第二个参数callback调用base::BindOnce创建时将this即FileWriterImpl转化为了base::Unretained(this)即纯指针(raw pointer),导致即使FileWriterImpl被释放callback也会执行,但实际上此时访问FileWriterImpl是uaf的。这个洞patch的方法是将base::Unretained(this)改为了weak_ptr,即FileWriterImpl对象释放后weak_ptr指向的内存被释放为空,callback不会再被执行。

1
2
3
4
5
6
7
8
void FileWriterImpl::Write(uint64_t position,
blink::mojom::BlobPtr blob,
WriteCallback callback) {
blob_context_->GetBlobDataFromBlobPtr(
std::move(blob),
base::BindOnce(&FileWriterImpl::DoWrite, base::Unretained(this),
std::move(callback), position));
}

GetBlobDataFromBlobPtr中主要调用GetInternalUUID(callback),其中callback使用了一个lamda表达式绑定并依次传参执行。由于//src/third_party/blink/public/mojom/blob/中提供了blob的js接口,我们可以通过在js中调用getInternalUUID释放掉FileWriterImpl对象,进而导致lamda表达式中回调执行时FileWriterImpl是uaf的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void BlobStorageContext::GetBlobDataFromBlobPtr(
blink::mojom::BlobPtr blob,
base::OnceCallback<void(std::unique_ptr<BlobDataHandle>)> callback) {
DCHECK(blob);
blink::mojom::Blob* raw_blob = blob.get();
raw_blob->GetInternalUUID(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindOnce(
[](blink::mojom::BlobPtr, base::WeakPtr<BlobStorageContext> context,
base::OnceCallback<void(std::unique_ptr<BlobDataHandle>)> callback,
const std::string& uuid) {
if (!context || uuid.empty()) {
std::move(callback).Run(nullptr);
return;
}
std::move(callback).Run(context->GetBlobDataFromUUID(uuid));
},
std::move(blob), AsWeakPtr(), std::move(callback)),
""));
}