首页 > 其他分享 >The Chrome Sandbox Part 2 of 3: The IPC Framework

The Chrome Sandbox Part 2 of 3: The IPC Framework

时间:2023-10-13 13:02:09浏览次数:40  
标签:function IPC Chrome messages process Sandbox message data


This post is the second part of a 3-part series about the Chrome sandbox. In the first post, I presented a basic overview of the Chrome process architecture and presented a breakdown of the attack surfaces for performing privilege escalations. This post continues our exploration of Chrome by focusing on one of the major attack surfaces identified - the IPC framework. As detailed in the previous post, this framework is used by Chrome to expose functionality to other processes by exporting a number of callback methods that client processes may invoke, much in the same way that traditional RPC client/server interaction occurs. This post discusses the inner workings of the IPC framework - a background to how it works, how messages are serialized and routed, and how to enumerate the attack surface to find processing exposed to untrusted  inputs. Several vulnerabilities that were uncovered during my audit are also presented to help illustrate what kind of vulnerabilities can occur at various levels of process interaction.



IPC Framework

Chrome processes communicate with each other via an Inter-Process Communication (IPC) messaging framework built in to the browser. For Windows, named pipes are utilized, whereas linux builds use local Unix sockets. Most of the code that implements the IPC framework is located within the ipc/ directory in the Chrome source tree. Apart from external OS facilities (such as the kernel), the IPC interaction between Chrome processes represents the largest local attack surface for potential privilege escalation. In the event that a renderer process is compromised, it can provide arbitrary requests to other more privileged processes that either trigger a vulnerability in one of the privileged processes, or cause them to perform a restricted operation on behalf of the renderer that inadvertently exposes the system to further compromise. So, let's take a look at how it works!

Channels

The messaging framework provided by Chrome provides bidirectional communication channels between two processes through the use of a Channel object (defined in ipc/ipc_channel.cc ). The Channel object has two key member objects of interest to us:


  1. channel_impl_ - A ChannelImpl object that actually implements the low level communications facilities.
  2. listener_ - A listener object whose OnMessageReceived() function is invoked whenever a message is successfully received from the communication channel. (The class definition of Listener is defined in ipc/ipc_channel.h , but several other classes derive from it.)

As indicated above, a Listener object is a special object that is associated with a channel that handles incoming messages. It is used to provide the desired functionality of a given process by exposing a number of method callbacks that are invoked via the Listener::OnMessageReceived() function. This function typically does not handle many of the messages it receives itself; instead, it routes the messages to one of a series of objects that has been previously registered with the Listener object in question. Each of these objects registered to the listener are uniquely identified by an ID known as a routing ID, and contain a series of numbered callback methods that clients may invoke. The number of the callback is unique to each object and is referred to as a message type. In the case of the browser process, the Listener object maintains a list of MessageFilter objects. The following diagram shows this arrangement:




As you can see, each method may be uniquely identified by a pair of IDs: A routing ID and a message type ID . We will see a bit later on when we look at the message format that this pair of IDs is present in the Message header, allowing messages to be routed with a simple two-step process:


  1. If the routing ID of the message is the special value MSG_ROUTING_CONTROL (0x7FFFFFFF), then the listener object itself is intended to handle the message. Otherwise, the message is routed to an appropriate object registered with the Listener that has a matching routing ID. If no registered object has the requested routing ID, an error is returned.
  2. Assuming an appropriate object has been located from step 1, a relevant callback function encapsulated within the selected object is selected based on the type value within the Message object.

Note



There is also a special case message sent at the initiation of a connection: it has the special routing value MSG_ROUTING_NONE (0xFFFFFFFE) and type HELLO_MESSAGE_TYPE (0xFFFF). This message is used to inform each side of the connection with the other's process ID.





Listener objects and registered message handling objects have an OnMessageReceived() function, which matches message types with callback functions using the IPC_MESSAGE_HANDLER() or IPC_MESSAGE_HANDLER_DELAY_REPLY() macros, as shown:


bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& msg) {
 
  
... code ...
 
  
IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWindow, OnMsgCreateWindow)
 
  
IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWidget, OnMsgCreateWidget)
 
  
IPC_MESSAGE_HANDLER(ViewHostMsg_SetCookie, OnSetCookie)
 
  
IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetCookies, OnGetCookies)
 
  
IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetRawCookies, OnGetRawCookies)
 
  
IPC_MESSAGE_HANDLER(ViewHostMsg_DeleteCookie, OnDeleteCookie)
 
  
IPC_MESSAGE_HANDLER(ViewHostMsg_GetCookiesEnabled, OnGetCookiesEnabled)
 
  
#if defined(OS_WIN)  // This hack is Windows-specific.
 
  
IPC_MESSAGE_HANDLER(ViewHostMsg_LoadFont, onl oadFont)
 
  
#endif
 
  
IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetPlugins, OnGetPlugins)
 
  
IPC_MESSAGE_HANDLER(ViewHostMsg_GetPluginPath, OnGetPluginPath)
 
  
IPC_MESSAGE_HANDLER(ViewHostMsg_DownloadUrl, OnDownloadUrl)
 
  
IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_ContextMenu,
 
  
                            OnReceiveContextMenuMsg(msg))
 
  
IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_OpenChannelToPlugin,
 
  
                                OnOpenChannelToPlugin)
 
  
IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_LaunchNaCl, OnLaunchNaCl)
 
  
IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWorker, OnCreateWorker)
 
  
IPC_MESSAGE_HANDLER(ViewHostMsg_LookupSharedWorker, OnLookupSharedWorker)
 
  
IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentDetached, OnDocumentDetached)
 
  

 
  
... code ...
 
  
}


The first parameter is a callback class name that contains an ID member that signifies the message type, and the second parameter is the callback function to invoke. We will discuss callback classes in more detail further on, but for now we just need to be aware that this class is used to associate a message type to a callback function. There are a vast number of message types exposed by each different type of host, each with unique sets of parameters and return values. For convenience, these message types are declared (along with their parameter types) in the following files:


chrome/common/render_messages_internal.h

  • ViewMsg_* - d efines messages sent by the browser process to a renderer process
  • ViewHostMsg_* - defines messages sent by a renderer process to the browser process

chrome/common/plugin_messages_internal.h


  • PluginProcessMsg_* - defines messages sent from the browser to the plugin process
  • PluginProcessHostMsg_ * - defines messages sent from thepluginprocess to the browser
  • PluginMsg_ * - defines messages sent from the renderer to the plugin process
  • PluginHostMsg_ * - defines messages sent from the plugin process to the renderer process
  • NPObjectMsg_ * - defines messages for dealing with NP Objects across process boundaries. Exchanged by both the renderer and the plugin process

chrome/common/worker_messages_internal.h


  • WorkerProcessMsg_ * - defines messages sent from the browser to the worker process
  • WorkerProcessHostMsg_ * - defines messages sent from the worker process to the browser
  • WorkerMsg_ * - defines messages sent from the renderer process to the worker process
  • WorkerHostMsg_ * - defines messages sent from the worker process to the renderer

chrome/common/utility_messages_internal.h


  • UtilityMsg_ * - defines messages from the browser to the utility process
  • UtilityHostMsg_ * - defines messages from the utility process to the browser


chrome/common/gpu_messages_internal.h


  • GpuMsg_ * - defines messages sent from the browser to the GPU process
  • GpuHostMsg_ * - defines messages sent from the GPU process to the browser
  • GpuChannelMsg_ * - defines messages sent from the renderer process to the GPU process
  • GpuCommandBufferMsg_ * - defines messages sent from the renderer process to the GPU process


chrome/common/nacl_messages_internal.h


  • NaClProcessMsg_ *- defines messages sent from the browser to the NaCl process



Inspection of these files will show that a message type is declared with the

IPC_MESSAGE_CONTROLXXX(),

IPC_MESSAGE_ROUTEDXXX() ,

IPC_MESSAGE_CONTROLXXX_YYY() , or

IPC_MESSAGE_ROUTEDXXX_YYY() macros (where XXX and YYY can be 0 - 4, indicating the number of parameters a message receives and returns respectively). Some example declarations are shown.


// 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:




  1. Is there a way to call certain functions in an unexpected way due to an expected sequence of IPC calls?
  2. 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.

标签:function,IPC,Chrome,messages,process,Sandbox,message,data
From: https://blog.51cto.com/u_16125990/7844030

相关文章

  • HarmonyOS跨进程通信—IPC与RPC通信开发指导
    HarmonyOS跨进程通信—IPC与RPC通信开发指导一、IPC与RPC通信概述基本概念IPC(Inter-ProcessCommunication)与RPC(RemoteProcedureCall)用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。需要跨进程通信的原因是因为......
  • Chrome之F12小技巧
    悬停定位右键检查通过快捷键ctrl+shift+c firefox点击inspector后一样可以出现悬停效果 TRANSLATEwithxEnglishArabicHebrewPolishBulgarianHindiPortugueseCatalanHmongDawRomanianChineseSimplifiedHungarianRussian......
  • python chromedriver下载与安装方法
    当需要进行自动化测试或爬取网页数据时,Chrome浏览器的驱动程序(ChromeDriver)是一个关键组件。它允许控制和与Chrome浏览器进行交互。在本文中,将为介绍如何下载和安装ChromeDriver。什么是ChromeDriverChromeDriver是一个用于自动化控制和与GoogleChrome浏览器进行交互的驱动程序。......
  • Python selenium chrome版本查询和对应驱动下载
    elenium爬虫需要安装Chrome驱动chrome版本查询和对应驱动下载,超详细方法/步骤1查看谷歌的版本,第一步在地址栏输入图中网址第二步查看版本号2复制版本号,只需复制版本号最后一位小数点之前的数字。(例:版本号:111.0.5563.65,复制111.0.5563即可)将复制的版本号......
  • Chrome安装后打不开任何页面 & 改名后图标变成小白块
    【网上最简单】Chrome安装后打不开任何页面&改名后图标变成小白块[30秒解决] 自从76版本后,我发现Chrome更新后,打不开任何网页,我就再也没有升级,停留在76.0.3809.87。最近Chrome大版本升级到了85,网上说是“史诗级”增强,我就心动了,但是升级后,然e...还是和以前一样,不是说升级了......
  • 在hadoop虚拟机里面使用hadoop jar运行打包文件,出现Exception in thread "main" org.a
    问题描述更改了JDK版本之后,再次运行又出现了这个错误:问题解决经过查阅相关资料,发现是自己定义的hdfs的路径不太对,本来写的是这样的:然后自己确实不记得配置环境时配置的是多少,就看了看这个文件core.site.xml:catcore-site.xml然后看到这里:使用的端口号是8020,改成跟环境......
  • 前端判断视频格式(H264、H265,解决谷歌chrome浏览器无法播放mp4问题)
    chrome浏览器对某些mp4文件不支持,播放时只有声音没有画面。这种情况一般是因为视频文件为H265编码,而chrome浏览器只支持H264编码的文件(谷歌没买H265的使用专利)。 解决方法一:使用软件(格式工厂或命令行库ffmpeg)对文件进行转换,将H265转换成H264。格式工厂: ffmpeg:ffmpeg-i......
  • chrome浏览器显示网页字体不正常,需要放大150% 显示正常
    软件环境:windows764位、chrome109.0.5414.120。故障现象:chrome浏览器,浏览百度、淘宝等网站总是需要把显示分辩率放大150%,然后显示正常。解决方案:下载windows7 kb2670838补丁安装后,重启电脑,打开chrome浏览器浏览网页显示正常。 ......
  • chrome调试工具之[hover悬浮样式]
    前言在调节带有hover样式的元素时,当鼠标移动到上面的时候才会显示相应的元素,鼠标移开时元素就会消失,所以导致无法调节样式问题,第一种方式方便定位元素位置,第二种方式可以解决问题(可以直接看第二种方法!!!)一、快捷定位元素位置的方法使用网上的方法,首先F12打开调试工具,找到加了h......
  • 水星 Mercury MIPC251C-4 网络摄像头 ONVIF 与 PTZ 云台控制
    概况最近在什么值得买上发现一款水星的网络摄像头,除了支持云台/夜视功能之外,还标明支持onvif协议.所以想着买来接入到HomeAssistat作为监控使用.可到手之后发现事情并没有那么简单,记录如下.接入HomeAssistant按照HA的文档 ONVIFCamera 接入无非就是配置文......