exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

Chrome SandboxedUnpacker Unsafe Shared Memory Use

Chrome SandboxedUnpacker Unsafe Shared Memory Use
Posted Jun 14, 2021
Authored by Google Security Research, Mark Brand

SandboxedUnpacker in Chrome uses shared memory in an unsafe fashion.

tags | advisory
SHA-256 | bc91dd004d418d7fd6b56285f99323944f8802e8dd4b5215b649c990046ed88a

Chrome SandboxedUnpacker Unsafe Shared Memory Use

Change Mirror Download
Chrome: SandboxedUnpacker unsafe use of shared memory.

If we look at the mojo interface gzipper.mojom (services/data_decoder/public/mojom/gzipper.mojom):

// An interface that lets callers compress and uncompress data using gzip.
interface Gzipper {
// Compresses |data| using gzip and returns it as |compressed_data|. Returns
// null if compression fails.
Compress(mojo_base.mojom.BigBuffer data)
=> (mojo_base.mojom.BigBuffer? compressed_data);

// Uncompresses |compressed_data| using gzip and returns it as |data|. Returns
// null if uncompression fails.
Uncompress(mojo_base.mojom.BigBuffer compressed_data)
=> (mojo_base.mojom.BigBuffer? data);
};

It's interesting already since the service appears to be doing a gzip decompression
directly from data in shared memory. However, this is architected so that the caller
of these interfaces is (at present) always a trusted process, and that the service
implementing this service is a less-privileged sandboxed utility service.

We can see that Uncompress returns a BigBuffer, which may be backed by shared
memory. If we look at the callsites for DataDecoder::GzipUncompress, this leads
us to SandboxedUnpacker:

void SandboxedUnpacker::UnzipDone(const base::FilePath& zip_file,
const base::FilePath& unzip_dir,
const std::string& error) {
DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());

if (!error.empty()) {
ReportFailure(SandboxedUnpackerFailureReason::UNZIP_FAILED,
l10n_util::GetStringUTF16(IDS_EXTENSION_PACKAGE_UNZIP_ERROR));
return;
}
base::FilePath verified_contents_path =
file_util::GetVerifiedContentsPath(extension_root_);
// If the verified contents are already present in the _metadata folder, we
// can ignore the verified contents in the header.
if (compressed_verified_contents_.empty() ||
base::PathExists(verified_contents_path)) {
Unpack(unzip_dir);
return;
}
data_decoder_.GzipUncompress(
compressed_verified_contents_,
base::BindOnce(&SandboxedUnpacker::OnVerifiedContentsUncompressed, this,
unzip_dir));
}

Once this receives the response from the DataDecoder, it passes the memory
returned in the BigBuffer directly into StoreVerifiedContentsInExtensionDir:

void SandboxedUnpacker::OnVerifiedContentsUncompressed(
const base::FilePath& unzip_dir,
data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result) {
DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
if (!result.value) {
ReportFailure(
SandboxedUnpackerFailureReason::
CRX_HEADER_VERIFIED_CONTENTS_UNCOMPRESSING_FAILURE,
l10n_util::GetStringFUTF16(
IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
ASCIIToUTF16(
\"CRX_HEADER_VERIFIED_CONTENTS_UNCOMPRESSING_FAILURE\")));
return;
}
if (!StoreVerifiedContentsInExtensionDir(result.value.value().byte_span()))
return;
Unpack(unzip_dir);
}

bool SandboxedUnpacker::StoreVerifiedContentsInExtensionDir(
base::span<const uint8_t> verified_contents) {
DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());

if (!VerifiedContents::Create(
ContentVerifierKey(kWebstoreSignaturesPublicKey,
kWebstoreSignaturesPublicKeySize),
{reinterpret_cast<const char*>(verified_contents.data()),
verified_contents.size()})) {
ReportFailure(SandboxedUnpackerFailureReason::MALFORMED_VERIFIED_CONTENTS,
l10n_util::GetStringFUTF16(
IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
ASCIIToUTF16(\"MALFORMED_VERIFIED_CONTENTS\")));
return false;
}

//...

Following this leads us further down:

std::unique_ptr<VerifiedContents> VerifiedContents::Create(
base::span<const uint8_t> public_key,
base::StringPiece contents) {
ScopedUMARecorder<kUMAVerifiedContentsInitTime,
kUMAVerifiedContentsInitResult>
uma_recorder;
// Note: VerifiedContents constructor is private.
auto verified_contents = base::WrapUnique(new VerifiedContents(public_key));
std::string payload;
if (!verified_contents->GetPayload(contents, &payload))
return nullptr;

And the first thing that VerifiedContents::GetPayload does is to parse this
data as JSON. Note that at this point, the StringPiece contents is still backed
by shared memory.

bool VerifiedContents::GetPayload(base::StringPiece contents,
std::string* payload) {
base::Optional<base::Value> top_list = base::JSONReader::Read(contents);
if (!top_list || !top_list->is_list())
return false;

I haven't verified that base::JSONReader does anything dangerous when given an
input backed by shared memory; it actually looks like most of the code in there
will manage to handle this unexpected situation fairly gracefully, although it
definitely seems to be outside of the intended usage (and eg. ICU code comes in
to play here as well...)

In any case, the next thing that StoreVerifiedContentsInExtensionDir does is
(after verifying the signatures and stuff in the JSON it parsed) to write the
contents of the shared memory out to disk; the attacker could change those
contents between the JSON parse and the final version being written to disk,
and they should be able to effectively bypass all of the checks in
VerifiedContents::Create.

bool SandboxedUnpacker::StoreVerifiedContentsInExtensionDir(
base::span<const uint8_t> verified_contents) {
DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());

if (!VerifiedContents::Create(
ContentVerifierKey(kWebstoreSignaturesPublicKey,
kWebstoreSignaturesPublicKeySize),
{reinterpret_cast<const char*>(verified_contents.data()),
verified_contents.size()})) {
ReportFailure(SandboxedUnpackerFailureReason::MALFORMED_VERIFIED_CONTENTS,
l10n_util::GetStringFUTF16(
IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
ASCIIToUTF16(\"MALFORMED_VERIFIED_CONTENTS\")));
return false;
}

base::FilePath metadata_path = extension_root_.Append(kMetadataFolder);
if (!base::CreateDirectory(metadata_path)) {
ReportFailure(
SandboxedUnpackerFailureReason::COULD_NOT_CREATE_METADATA_DIRECTORY,
l10n_util::GetStringFUTF16(
IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
ASCIIToUTF16(\"COULD_NOT_CREATE_METADATA_DIRECTORY\")));
return false;
}

base::FilePath verified_contents_path =
file_util::GetVerifiedContentsPath(extension_root_);

// Cannot write the verified contents file.
if (!base::WriteFile(verified_contents_path, verified_contents)) { // <-- verified_contents is still pointing to the shared memory
ReportFailure(
SandboxedUnpackerFailureReason::
COULD_NOT_WRITE_VERIFIED_CONTENTS_INTO_FILE,
l10n_util::GetStringFUTF16(
IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
ASCIIToUTF16(\"COULD_NOT_WRITE_VERIFIED_CONTENTS_INTO_FILE\")));
return false;
}

return true;
}

This bug is subject to a 90 day disclosure deadline. After 90 days elapse,
the bug report will become visible to the public. The scheduled disclosure
date is 2021-06-11. Disclosure at an earlier date is possible if
agreed upon by all parties.





Found by: markbrand@google.com

Login or Register to add favorites

File Archive:

May 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    May 1st
    44 Files
  • 2
    May 2nd
    5 Files
  • 3
    May 3rd
    11 Files
  • 4
    May 4th
    0 Files
  • 5
    May 5th
    0 Files
  • 6
    May 6th
    28 Files
  • 7
    May 7th
    3 Files
  • 8
    May 8th
    4 Files
  • 9
    May 9th
    54 Files
  • 10
    May 10th
    12 Files
  • 11
    May 11th
    0 Files
  • 12
    May 12th
    0 Files
  • 13
    May 13th
    17 Files
  • 14
    May 14th
    11 Files
  • 15
    May 15th
    17 Files
  • 16
    May 16th
    13 Files
  • 17
    May 17th
    22 Files
  • 18
    May 18th
    0 Files
  • 19
    May 19th
    0 Files
  • 20
    May 20th
    17 Files
  • 21
    May 21st
    0 Files
  • 22
    May 22nd
    0 Files
  • 23
    May 23rd
    0 Files
  • 24
    May 24th
    0 Files
  • 25
    May 25th
    0 Files
  • 26
    May 26th
    0 Files
  • 27
    May 27th
    0 Files
  • 28
    May 28th
    0 Files
  • 29
    May 29th
    0 Files
  • 30
    May 30th
    0 Files
  • 31
    May 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close