记录分析过的一些典型的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结构体名称是
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
或者直接使用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)), "")); }