// Used to set a cookie. The cookie is set asynchronously, but will be
// available to a subsequent ViewHostMsg_GetCookies request.
IPC_MESSAGE_ROUTED3(ViewHostMsg_SetCookie,
GURL /* url */,
GURL /* first_party_for_cookies */,
std::string /* cookie */)
// Used to get raw cookie information for the given URL. This may be blocked
// by a user prompt to validate a previous SetCookie message.
IPC_SYNC_MESSAGE_ROUTED2_1(ViewHostMsg_GetRawCookies,
GURL /* url */,
GURL /* first_party_for_cookies */,
std::vector<webkit_glue::webcookie></webkit_glue::webcookie>
/* raw_cookies */)
// Used to delete cookie for the given URL and name
IPC_SYNC_MESSAGE_CONTROL2_0(ViewHostMsg_DeleteCookie,
GURL /* url */,
std::string /* cookie_name */)
We will revisit these macros a bit later on, but for now we
can see that cross-referencing all the defined message type classes from
the above-mentioned files with where they are associated with a
callback function (ie. where the message class is used as a parameter to
the IPC_MESSAGE_HANDLER()
macro), we can find the implementation
of each routine exposed over IPC, what data types they take, and what
data they return. (We will discuss the marshalling of such parameters
shortly.) Therefore, we are able to enumerate the exposed function
attack surface and review each callback for possible flaws. For each of
these functions, we must be mindful of typical vulnerabilities related
to integer manipulation, dangerous memory management patterns (use after
free etc), and standard buffer or pointer-related errors.
Example 1 - Worker Process Message Forwarding
Here is an example of a memory manipulation error that can be
triggered in the browser process by a sandboxed renderer process.
Chrome has the notion of 'Worker Processes' (which are also sandboxed)
which renderers may start by sending the browser a ViewHostMsg_CreateWorker
message. It may then subsequently send arbitrary messages destined for
the worker process that are routed through the privileged browser
process. The code to perform this forwarding is implemented in the WorkerProcessHost::OnMessageReceived()
function (from chrome/browser/worker_host/worker_process_host.cc
):
for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
if (i->worker_route_id() == message.routing_id()) {
if (!i->shared()) {
// Don't relay messages from shared workers (all communication is via
// the message port).
WorkerInstance::SenderInfo info = i->GetSender();
CallbackWithReturnValue<int>::Type* next_route_id =
GetNextRouteIdCallback(info.first);
RelayMessage(message, info.first, info.second, next_route_id);
}
if (message.type() == WorkerHostMsg_WorkerContextDestroyed::ID) {
instances_.erase(i);
UpdateTitle();
}
break;
}
}
Assuming an appropriate worker process has been found, the RelayMessage() function is called:
void WorkerProcessHost::RelayMessage(
const IPC::Message& message,
IPC::Message::Sender* sender,
int route_id,
CallbackWithReturnValue<int>::Type* next_route_id)
<int>{
if (message.type() == WorkerMsg_PostMessage::ID) {
// We want to send the receiver a routing id for the new channel, so
// crack the message first.
string16 msg;
std::vector<int> sent_message_port_ids;
std::vector<int> new_routing_ids;
if (!WorkerMsg_PostMessage::Read(
&message, &msg, &sent_message_port_ids, &new_routing_ids)) {
return;
}
DCHECK(sent_message_port_ids.size() == new_routing_ids.size());
for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
new_routing_ids[i] = next_route_id->Run();
MessagePortDispatcher::GetInstance()->UpdateMessagePort(
sent_message_port_ids[i], sender, new_routing_ids[i], next_route_id);
}
The code here reads two vectors from the input message and proceeds to fill one of them (
new_routing_ids
) out. The loop featured above will write elements in to
new_routing_ids
according to the size of
send_message_port_ids
. Therefore, if
new_routing_ids
is smaller than
sent_message_port_ids
,
then this loop will end up writing data to an out of bounds memory
location, resulting in a buffer overflow. This bug of course hinges on
the fact that the vector operator[] function does not do validation on
the index passed to it, which is the case for g++, but not for Microsoft
STL implementations. This bug was fixed in February of this year (
http://src.chromium.org/viewvc/chrome?view=rev&revision=38209
).
Note
The
code above appears to do a sanity check with the DCHECK() line
preceding the loop, however closer inspection will reveal that DCHECK()
assertions are only present in debug builds, and are not compiled in to
release builds.
In addition to memory corruption, we must also keep in mind
the higher-level consequences of being able to randomly call any of
these functions:
- Is there a way to call certain functions in an unexpected way due to an expected sequence of IPC calls?
- Does the function perform any operation that would represent a
potential security problem? For example, will it let us write arbitrary
files to locations that we couldn't normally write to due to sandbox
restrictions?
Example 2 - Clipboard Type Confusion Vulnerability
This bug is actually a type confusion vulnerability but stems
from an unexpected invocation of one of the standard clipboard messages
that a renderer may send a browser. The browser exposes two functions
for writing to the clipboard - ViewHostMsg_ClipboardWriteObjectsSync
and ViewHostMsg_ClipboardWriteObjectsAsync
. They are both exposed to IPC via the ResourceMessageFilter
object (implemented in chrome/browser/renderer_host/resource_message_filter.cc
).
As their respective names imply, they are used to write objects to the
clipboard - with the former function performing a synchronous write and
the latter performing an asynchronous one. In each case, the parameter
passed by the user is a std::map < int, vector <> >
. For each pair in the map, the integer value denotes the type of object to be written to the clipboard, and the vector <>
is essentially a buffer containing the contents. The function that stores the data in the clipboard is the Clipboard::DispatchObject()
function, which performs sanity checking on the data for various object
types before storing them. The object type of interest to us is the CBF_SMBITMAP
type:
case CBF_SMBITMAP: {
using base::SharedMemory;
using base::SharedMemoryHandle;
if (params[0].size() != sizeof(SharedMemory*))
return;
// It's OK to cast away constness here since we map the handle as
// read-only.
const char* raw_bitmap_data_const =
reinterpret_cast<const char*>(&(params[0].front()));
char* raw_bitmap_data = const_cast<char*>(raw_bitmap_data_const);
scoped_ptr<SharedMemory> bitmap_data(
*reinterpret_cast<SharedMemory**>(raw_bitmap_data));
if (!ValidateAndMapSharedBitmap(params, bitmap_data.get()))
return;
WriteBitmap(static_cast<const char*>(bitmap_data->memory()),
&(params[1].front()));
break;
}
As can be seen, the buffer contents supplied by the user is actually interpreted as a pointer to a SharedMemory
object.
Thus, it would seem that the user is able to specify an arbitrary
pointer that is interpreted as a pointer to an object. However, this is
not entirely correct. We see that in ResourceMessageFilter::OnClipboardWriteObjectsSync()
(the function exposed to IPC For ViewHostMsg_ClipboardWriteObjectsSync
), the buffer supplied by the user for CBF_SMBITMAP
where the pointer is taken from is actually replaced with a valid pointer by the browser process.
void ResourceMessageFilter::OnClipboardWriteObjectsSync(
const Clipboard::ObjectMap& objects,
base::SharedMemoryHandle bitmap_handle)
{
DCHECK(base::SharedMemory::IsHandleValid(bitmap_handle))
<< "Bad bitmap handle";
// We cannot write directly from the IO thread, and cannot service the IPC
// on the UI thread. We'll copy the relevant data and get a handle to any
// shared memory so it doesn't go away when we resume the renderer, and post
// a task to perform the write on the UI thread.
Clipboard::ObjectMap* long_living_objects = new Clipboard::ObjectMap(objects);
// Splice the shared memory handle into the clipboard data.
Clipboard::ReplaceSharedMemHandle(long_living_objects, bitmap_handle,
handle());
ChromeThread::PostTask(
ChromeThread::UI,
FROM_HERE,
new WriteClipboardTask(long_living_objects));
}
The actual replacement is performed by Clipboard::ReplaceSharedMemHandle().
The problem is that objects of type CBF_SMBITMAP
are only ever expected to appear in ViewHostMsg_ClipboardWriterObjectsSyncmessages
, and not expected in ViewHostMsg_ClipboardWriteObjectsAsync
messages. Therefore, the Clipboard::ReplaceSharedMemHandle()
function is never called for the latter:
void ResourceMessageFilter::OnClipboardWriteObjectsAsync(
const Clipboard::ObjectMap& objects)
{
// We cannot write directly from the IO thread, and cannot service the IPC
// on the UI thread. We'll copy the relevant data and post a task to preform
// the write on the UI thread.
Clipboard::ObjectMap* long_living_objects = new Clipboard::ObjectMap(objects);
ChromeThread::PostTask(
ChromeThread::UI,
FROM_HERE,
new WriteClipboardTask(long_living_objects));
}
Therefore, by calling the
ViewHostMsg_ClipboardWriteObjectAsync
method from a renderer with a
CBF_SMBITMAP
object
in our parameter array, it is possible to provide an arbitrary pointer
that will be later cast to an object and utilized, thus resulting in
potential arbitrary execution. This bug was fixed in June of this year (
http://src.chromium.org/viewvc/chrome?view=rev&revision=46639
).
You should keep in mind that the attack surface might also
include IPC functions exposed by the local unprivileged process. If the
local process exposes methods that return data to a more privileged
process, they might be able to supply malformed or unexpected responses
that will result in vulnerabilities when being processed by the client.
Messages
So far we have looked at the functions exposed over IPC, how
to enumerate them, and how to determine what parameters may be passed to
them. But we skipped some important details - how exactly are these
functions invoked? And how are parameters transported from one process
to the other? The answer to these questions requires us to look at some
of the lower level transmission details a little more closely, which we
will do here.
The basic unit for transmission across IPC channels is the Message object (defined in ipc/ipc_message.h
). Message objects are structured as follows.
Messages are processed in the order they are received from the communications channel by the ChannelImpl::ProcessIncomingMessage()
function (implemented within ipc/ipc_channel_win.cc
or ipc/ipc_channel_posix.cc
depending
on the target platform). This is perhaps the lowest-level input vector
(save for OS-level problems) for targeting higher-privileged processes
via the IPC framework. Here, we can examine how messages are received,
buffered, and decapsulated. We might expect to find integer-related
errors due to message lengths, out-of-state errors problems due to
unexpected flag fields and such (which we didn't cover here - this is an
exercise left to the reader), or poor descriptor manipulation for Unix
implementations.
As we already know, once messages have been successfully
received, they are routed to an appropriate callback function through
the use of a routing ID and message type, both of which are present in
the Message header. The IPC_MESSAGE_HANDLER()
macros actually
expand to a case statement based on the message type. The remainder of
the message data is interpreted as a set of parameters for the called
function, or for results of a called function in the case of reply
messages.
Parameter Deserialization
Previously, we looked at how callback classes were associated with callback routines exposed over IPC. We also introduced the IPC_MESSAGE_ROUTED()
families of macros that indicate the parameters that each function will
take. These macros actually build definitions of the named callback
classes based on the parameters provided to the macro. Callback classes
indicated by the first parameter are all derived from IPC::Message
or IPC::MessageWithTuple < >
and
contain key elements needed to invoke the callback function correctly.
Firstly, each object contains the aforementioned static ID member that
is used to match messages type IDs to desired functions, and secondly, a
Dispatch()
method is implemented for each class that performs
the necessary parameter deserialization from the message blob and passes
the resultant parameters to the destination callback function. Note
that in the case of callback functions with no parameters, the
IPC::Message class is used, which does not need to do any parameter
deserialization. Otherwise, IPC::MessageWithTuple < >
is derived from, with the data types of the expected parameters indicated as template parameters to the class.
So, the MessageWithTuple < >::Dispatch()
function must unpack parameters intended for the callback function from the message data. This is achieved with the MessageWithTuple < >::Read()
function, which uses several layers of templating indirection to call the correct deserialization routine. Likewise, MessageWithTuple < >::Write()
will cause relevant output parameters to be written to a message for transmitting a result. The Message
class derives from the Pickle
class (implemented in base/pickle.cc
),
which contains functions for reading basic data types such integers,
strings, and bools. Callbacks that take basic types as parameters
essentially use these functions directly.
For more complicated data structures, unpacking and packing is achieved by calling another templatized method: ParamTraits <> ::Read()
and ParamTraits <> ::Write()
,
where 'type' is the expected data structure being read or written. So,
for every possible data structure received by a callback function, there
is a matching ParamTraits <>
implementation. An example for the gfx::Point
object is shown:
bool ParamTraits<gfx::Point>::Read(const Message* m, void** iter,
gfx::Point* r) {
int x, y;
if (!m->ReadInt(iter, &x) ||
!m->ReadInt(iter, &y))
return false;
r->set_x(x);
r->set_y(y);
return true;
}
void ParamTraits<gfx::Point>::Write(Message* m, const gfx::Point& p) {
m->WriteInt(p.x());
m->WriteInt(p.y());
}
Most of the ParamTraits < >
implementations are located within the following files:
chrome/common/common_param_traits.h
chrome/common/common_param_traits.cc
chrome/common/gpu_messages.h
chrome/common/plugin_messages.h
chrome/common/render_messages.h
chrome/common/utility_messages.h
chrome/common/webkit_param_traits.h
ipc/ipc_message_utils.cc
ipc/ipc_message_utils.h
The type deserialization routines are another broad attack
surface that can be targeted by untrusted processes looking to perform
privilege escalation attacks. There are a large number of data
structures that contain relatively complex data structures, and they
make likely candidates for memory corruption-style vulnerabilities,
especially due to integer manipulation problems.
Example 3 - SkBitmap Deserialization
SkBitmap
structures are used to transmit bitmap data between processes over IPC. The SkBitmap
structure looks like this:
class SkBitmap {
...
uint32_t fRowBytes;
uint32_t fWidth;
uint32_t fHeight;
uint8_t fConfig;
uint8_t fFlags;
uint8_t fBytesPerPixel; // based on config
};
ParamTraits < SkBitmap > ::Read()
allows a largely unchecked SkBitmap_Data
structure to be used to create an SkBitmap
:
bool ParamTraits<SkBitmap>::Read(const Message* m, void** iter, SkBitmap* r) {
const char* fixed_data;
int fixed_data_size = 0;
if (!m->ReadData(iter, &fixed_data, &fixed_data_size) ||
(fixed_data_size <= 0)) {
NOTREACHED();
return false;
}
if (fixed_data_size != sizeof(SkBitmap_Data))
return false; // Message is malformed.
const char* variable_data;
int variable_data_size = 0;
if (!m->ReadData(iter, &variable_data, &variable_data_size) ||
(variable_data_size < 0)) {
NOTREACHED();
return false;
}
const SkBitmap_Data* bmp_data =
reinterpret_cast<const SkBitmap_Data*>(fixed_data);
return bmp_data->InitSkBitmapFromData(r, variable_data, variable_data_size);
}
While the
allocPixels()
function (called from
InitSkBitmapFromData()
) protects from integer overflows, it does so by checking that
fHeight * fRowBytes
do not overflow. However,
fWidth
can be desynchronized from
fRowBytes
(that is,
fWidth
is not checked to be valid in relation to the
fHeight
and
fRowBytes
members), which in turn can create problems in the privileged process. For example, when the
SkBitmap
is stored in a Thumbnail store (via the
ViewHostMsg_Thumbnail
message), the
JPEGCodec
encodes the data from the
SkBitmap
, which does calculations based on the images'
fWidth
value, not
fRowBytes
. This bug was fixed in December of last year (
http://src.chromium.org/viewvc/chrome?view=rev&revision=35371
).
In addition to memory corruption-style vulnerabilities, it is
important to keep an eye out for data structures that maintain some
sort of internal state information. In this case, it might be possible
to alter that state in an unexpected way and cause processing
vulnerabilities due to the desynchronized structure.
Example 4 - Plugin Messages
Quite a large number of messages exchanged between the
renderer process and the plugin process were found to contain data
structures that had internal pointer fields. These pointers were never
used by the renderer (as they are only valid in the context of the
plugin process), but were transmitted back and forth as part of a way to
maintain state of persistent data structures. One such example is NPVariant_Params
structure defined in chrome/common/plugin_messages.h
. This data structure was defined as:
struct NPVariant_Param {
NPVariant_ParamEnum type;
bool bool_value;
int int_value;
double double_value;
std::string string_value;
int npobject_routing_id;
intptr_t npobject_pointer;
};
Essentially, it is used to transmit an NPVariant
object between processes as part of invocation or property getting/setting of plugin scriptable objects. The NPVariant
data structure is read using ParamTraits < NPVariant_Param > ::Read()
:
static bool Read(const Message* m, void** iter, param_type* r) {
... code ...
} else if (r->type == NPVARIANT_PARAM_OBJECT_ROUTING_ID) {
result =
ReadParam(m, iter, &r->npobject_routing_id) &&
ReadParam(m, iter, &r->npobject_pointer);
} else if (r->type == NPVARIANT_PARAM_OBJECT_POINTER) {
result = ReadParam(m, iter, &r->npobject_pointer);
} else if ((r->type == NPVARIANT_PARAM_VOID) ||
(r->type == NPVARIANT_PARAM_NULL)) {
result = true;
} else {
NOTREACHED();
}
return result;
}
As can be seen, the NPVariant_Params
passed as arguments to Invoke()
can contain arbitrary pointers if they are of type NPVARIANT_PARAM_OBJECT_ROUTING_ID
or NPVARIANT_PARAM_OBJECT_POINTER
. These pointers are subsequently treated as valid NPObject
pointers when passed to the destination plugin function. This can easily lead to arbitrary execution.
Handle Sharing
You will notice that there are numerous instances where
handles to objects are shared across processes. For Windows versions of
Chrome, passing handles to other processes is typically achieved using
the DuplicateHandle()
function, and then passing the resultant HANDLE
value to the remote process. For Unix, socket descriptors are passed over the local Unix socket using SCM_RIGHTS
ancillary
messages. The descriptors passed over the sockets are then referenced
by index from the array of socket descriptors received by the same
message. Most of the socket descriptor sanitation takes place in the ChannelImpl::ProcessIncomingMessage()
function
that was mentioned earlier. Passing resources between processes needs
to be done quite carefully, as potentially sensitive resources are being
handed to privileges of lesser privilege. I found an example of this
type of problem, however the Google Chrome security team had already
found it and patched it by the time I reported it. Still, it makes a
good case study, so we will briefly mention it here.
Example 5 - Database File Manipulation
Chrome provides a number of methods to manipulate database files, exposed through the DatabaseDispatcherHost
object(implemented in chrome/browser/renderer_host/database_dispatcher_host.cc
).
Several of these messages resulted in the opening of a database file of
the callers choosing, although the database file given would be
pre-pended with a fixed database path. The work for generating the
filename is implemented within DatabaseUtil::GetFullFilePathForVfsFile()
and DatabaseUtil::CrackVfsFilename
(both implemented in webkit/database/database_util.cc
).
Conclusion
The IPC framework is a large and ripe attack surface.
Hopefully this article has helped to demystify some of its inner
workings and provided a rough guide to how one might go about auditing
it. Tune in for my final post where I will discuss the sandbox
implementation itself, how it works, and also provide some examples of
vulnerabilities that were uncovered there.
相关推荐
「移动安全」IPC_You_Outside_the_Sandbox:One_bug_to_Rule_the_Chrome_Broker - 数据治理 防火墙 安全人才 APT 安全意识 应急响应
【标题】:“信息安全_数据安全_IPC_You_Outside_the_Sandbox:One_.pdf” 【描述】:“信息安全_数据安全_IPC_You_Outside_the_Sandbox:One_情报处理AI云数据库解决方案自动化” 该文档主要探讨了信息安全和数据...
Integrating the two types of circuitry can be challenging, but these designs can provide system-on-a-chip (SOC) functionality that, once designed into a product, can significantly impact final product...
"Chrome-extension-sandbox"指的是Chrome扩展的沙箱环境,这是一个安全机制,用于隔离扩展的脚本执行,防止恶意代码对用户系统造成损害。 在Chrome扩展开发中,沙箱模型是核心安全特性之一。它将扩展的执行环境分为...
赠送jar包:lucene-sandbox-6.6.0.jar; 赠送原API文档:lucene-sandbox-6.6.0-javadoc.jar; 赠送源代码:lucene-sandbox-6.6.0-sources.jar; 赠送Maven依赖信息文件:lucene-sandbox-6.6.0.pom; 包含翻译后的API...
在所有基于Chromium和Chrome的应用程序中都可以找到沙箱状态页面:chrome://沙盒查看您的Chrome沙盒状态:chrome:// sandbox / (右键单击,复制粘贴URL: Chrome甚至不允许您超链接到该页面! )危险基于...
The Sandbox The Class Loader Architecture The Class File Verifier Phase One: Internal Checks Phase Two: Verification of Symbolic References Safety Features Built Into the Java Virtual Machine ...
#Half-Life 2:Sandbox Team Andrew McWatters Henry Tran Matthew 半条命2:沙箱是对Source Engine的开源沙箱修改,它提供Lua作为脚本平台,供开发人员快速制作游戏创意原型。 简约游戏包括一个沙盒游戏模式,可...
### 部署openEuler+python3+chrome+selenium环境 #### 一、环境配置与准备工作 在本文中,我们将详细介绍如何在openEuler操作系统上部署Python3、Chrome及Selenium环境,以便进行Web自动化测试。openEuler是一款...
半条命2:沙盒 注意:这是原始存储库中的代码。 归功于原始创作者由于只读更改,我只能导入到Github 原始Qoutes: 自豪地由 阿德南的旋转重力枪 阿德南·扎法(Adnan Zafar) adnan.f.zafar AT gmail DOT com ...
【React-TS-Sandbox:StackBlitz上的High Voltage开发环境】 React-TS-Sandbox是一个基于StackBlitz的在线开发环境,特别为使用TypeScript编写React应用程序而设计。它结合了React,一个流行的JavaScript库用于构建...
赠送jar包:lucene-sandbox-7.2.1.jar; 赠送原API文档:lucene-sandbox-7.2.1-javadoc.jar; 赠送源代码:lucene-sandbox-7.2.1-sources.jar; 赠送Maven依赖信息文件:lucene-sandbox-7.2.1.pom; 包含翻译后的API...
Sandbox Sandbox 发 Sandbox Sandbox Sandbox
《藏经阁-The-Adventures-Of-Av-And-The-Leaky-Sandbox.pdf》这份文档探讨了安全领域的一个重要话题:云杀毒软件(AV)如何可能削弱企业端点的安全性。文章由Itzik Kotler和Amit Klein两位安全研究专家撰写,他们...
Windows沙箱(Windows Sandbox)是一种集成在Windows 10操作系统中的安全功能,它为用户提供了一个安全、独立且可快速还原的环境来运行应用程序。这个环境被称为“沙箱”,因为它将应用的运行限制在一个隔离的空间内...
git clone --recursive https://github.com/andydbc/unity-shadergraph-sandbox.git 现有存储库可以手动更新: git submodule init git submodule update 例子 溶解 火 全息图 像素化 香椿 简单标志 要求 ...
- The effectiveness of the proposed framework is demonstrated through real-world applications by porting and verifying several popular login modules into the framework. This approach validates the ...
赠送jar包:lucene-sandbox-7.7.0.jar; 赠送原API文档:lucene-sandbox-7.7.0-javadoc.jar; 赠送源代码:lucene-sandbox-7.7.0-sources.jar; 赠送Maven依赖信息文件:lucene-sandbox-7.7.0.pom; 包含翻译后的API...
赠送jar包:lucene-sandbox-7.3.1.jar; 赠送原API文档:lucene-sandbox-7.3.1-javadoc.jar; 赠送源代码:lucene-sandbox-7.3.1-sources.jar; 赠送Maven依赖信息文件:lucene-sandbox-7.3.1.pom; 包含翻译后的API...
【sandbox3完美修正官方版报错稀有补丁】是一个针对sandbox3软件的修复补丁,主要用于解决在使用过程中可能出现的错误和异常。Sandbox3是一款强大的虚拟化工具,它允许用户在一个隔离的环境中运行应用程序,以防止...