Analysis of vulnerabilities in Internet Explorer (CVE-2014-6350)

Source: Internet
Author: User
Tags mail code

Analysis of vulnerabilities in Internet Explorer (CVE-2014-6350)
0x00 Preface

 

This month, Microsoft fixed three sandbox bounce vulnerabilities in the IE enhanced protection mode, which were disclosed by me (the original author, the same below) in May August. Sandbox is the main focus of Project Zero (I also participated), and it is also the key point for attackers to implement a remote code attack.

All three bugs are fixed in the MS14-065 and you can read the article here.

CVE-2014-6350 may be the most interesting, not because the BUG is special, but because the technical points used when the BUG is to be exploited are unconventional. It is a read arbitrary memory vulnerability, but shows a potential attack method through the COM host. This blog post will introduce how to implement this vulnerability in depth.

 

0x01 what is this vulnerability?

The vulnerability is caused by the permission of the IE proxy process in enhanced protection mode. This vulnerability does not affect the old protection mode because I will introduce it later. An untrusted Tab process is running in the PEM sandbox because webpage content is running in the Tab process. The agent process is responsible for providing necessary permissions to the Tab process as needed. The Tab and proxy processes interact with each other through DCOM based on IPC communication.

After knowing how the Windows Access check works, we should be able to determine the permissions you want to obtain when opening the proxy process from the PEM sandbox. In AppContainer, code access check is more complex than a set of mechanisms used by Windows. In addition to common access checks, there are two independent additional checks for DACL calculation that provide maximum permissions. The first check is for the user and group SID in the Token, and the second is for the Compability SID. After the two sets of permissions are bitwise AND computed (* Note: intersection), the maximum permissions can be granted to the user (ACE is ignored here because it has nothing to do with this discussion ).

Let's take a look at the proxy process's DACL. Below is a simplified form. The first access check will match the current user's SID, that is, it will give full control (marked in red ), the second check will match IE's Compability SID (marked in blue). After the two permissions are obtained and set, only the "read and query" permission is available. In fact, this time Microsoft fixed the Read Memory permission.

We can call OpenProcess to pass in the proxy process PID and request the PROCESS_VM_READ permission. In this way, the kernel returns a handle to the process in the sandbox. With this handle, you can use ReadProcessMemory to read any memory of the proxy process. However, this function will correctly handle read-Invalid Memory operations, so there will be no crashes.

BOOL ReadMem(DWORD ppid, LPVOID addr, LPVOID result, SIZE_T size) {    HANDLE hProcess = OpenProcess(PROCESS_VM_READ,          FALSE,          ppid);    BOOL ret = FALSE;    if(hProcess) {        ret = ReadProcessMemory(hProcess,          addr,          result,          size,          NULL);        CloseHandle(hProcess);    }    return ret;}

However, if you use a Win64-bit system and execute this vulnerability from the 32-bit Tab process, the problem may be complicated because Wow64) you cannot directly use ReadProcessMemory to read the memory of a 64-bit proxy process. However, you can use modules such as wow64ext to bypass this restriction, but we do not care about it now.

Wait. Let's take a look at PM. Why is there no problem here? Only one access check will be performed in PM, so we can get full control, but we cannot do this because of the forced robustness check (IL) feature introduced after Microsoft Vista. When a process tries to open another process, the kernel first compares the visitor's IL and the system ACL of the target process. If the access process's IL level is lower than the target process's robustness level, the access permission is limited to a small subset of available permissions (such as PROCESS_QUERY_LIMITED_INFORMATION ). This will block PROCESS_VM_READ or more dangerous permissions, even if the DACL has been checked.

Okay, so let's look at the program running in the PEM sandbox in Process Explorer. We can clearly see that its Token is at a low robustness level (selected part ).

However, the AppContainer access check seems to have ignored any sub-level resources. If a resource passes the DACL check, it will be granted permissions regardless of IL. This seems to be effective for any security resources including files and registry keys. I don't know why it is designed like this, but it looks like a weakness. If IL is correctly checked here, it won't happen.

0x02 Vulnerability

Google event tracking? Id = 97) provides the original PoC and provides an idea of using the proxy's IPC interface to read arbitrary files in the system. By reading the HMAC key of each process, the PoC then forged a valid Token and opened the file through CShDocVwBroker: GetFileHandle. This is very useful for EPM, because AppContainer will prevent reading arbitrary files. However, in other words, this is just a read, not a write. Ideally, we should be able to completely break away from Sandbox, rather than just disclosing the content of some files.

It seems to be a difficult job, but in fact there are more ways to use each process secret value (per-process secrets) to change itself to a safer technology. One technology is my favorite Windows COM ). In the end, as long as we can expose the content of the host process, there is a way to introduce remote COM services to execute code in many processes.

COM thread model, suite and interface Mail Processing)

COM is used by multiple Windows components, such as Explorer Shell or local permission service, such as BITS. Each use case has different requirements and restrictions. For example, the UI requires all the code to run in one thread. Otherwise, the operating system will be uncomfortable (Notes: programmers are also upset ). On the other hand, a function class may be completely thread-safe. To support these requirements, COM supports a set of thread models, which reduces the burden on programmers (Not much ).

An object in the suite defines how the methods in the object are called. There are two types of suites: 1. Single-threaded and multi-threaded suites (MTA, for more information, see "COM Essence Theory". MTA is literal ). When considering how these suites are called, we need to define the relationship between callers and objects. Therefore, the caller's method is called "client" and the object is "server ".

The client's suite is determined by the flag passed to CoInitializeEx (Stas by default when CoInitialize is used. The server suite depends on the thread model definition of the COM object in the Windows registry. There are three possible settings: Free (multithreading), Apartment (single thread), and Both. If the client and server have compatible suites (only when the server object supports two thread models ), then, the function call that calls the object will be directly referenced to the corresponding function through the virtual function table of the object. However, when the STA calls the MTA or MTA to call the STA, we need to use some methods for proxy calls. COM handles such operations by sending mails. The following table is a summary.

The service end object is called through the serialization method of the process. This is especially important in Stas, because all things in Stas must be called in one thread. This is usually done by Windows message loop. If your program does not have a window or a message loop, STA will create (Using "hidden windows "). When a client calls an object in an incompatible suite, it actually calls a special proxy (: Proxy, not the broker above) object. This proxy object knows every failed COM interface and method, including the parameters required by the method.

After the proxy receives the parameters, it will serialize them through the built-in COM mail code and then send them to the server. The server side returns parameters through a call and then calls the appropriate method. Any return value is sent to the client in the same way.

The result is that this model is executed in the process just as well as using DCOM for inter-process operations. The same proxy blocking technology and dispatch (dispatcher) can be used between processes or computers. The only difference is the transmission of the sending parameters. Instead of operating in a single process, it uses local RPC, named pipe, or even based on the location of the client and server, use TCP.

Free-threaded receivaler)

Well, how does one leak information in the memory? To understand this, I need to introduce another thing called the "restriction thread blocking model" (FTM), which is related to the previous table. When the STA client calls a multi-thread-compatible server, it seems that it is very cost-effective for the client to communicate through the proxy-mail process. Why doesn't he directly call objects? This is the problem that FTM should solve.

When a COM object is instantiated from an incompatible suite reference, it must return an object reference to the caller. This is the same sending method as the normal call method. In fact, this mechanism is also applicable when the COM object is included in the parameter when the object method is called. This mechanism is used to pass the referenced sender to create a unique data stream called OBJREF. This data stream contains the information required by all clients to create a proxy object and contact the server. This example is the "pass by reference" syntax of the COM object. An example of OBJREF is as follows:

In some scenarios, although the content can be passed through the value, such as suspending the proxy. When all the code of the specified object in the original client suite needs to be re-constructed, the OBJREF stream can be transmitted using the value-based transfer syntax. When the unblocking sender re-constructs an object, it creates and initializes a new copy of the original object, instead of obtaining the proxy. An object can use its own value-based transfer syntax through the IMarshal interface.

This feature can be used by FRM to "Cheat" the operating system. That is, by passing an object that has been serialized in memory in OBJREF, rather than passing a pointer to the original object data. When unblocking is sent, the pointer is deserialized and returned to the caller. It acts like a "forged proxy" and allows sending requests directly to the original object.

It is understandable if you feel uncomfortable. Because the mails are different from those of DCOM, is COM a major security vulnerability in the process? Fortunately, this is not the case. FTM not only sends pointer values, but also ensures that the unblocking operation of the sent data is only executed in the same process. It generates a 16-byte random value of the process and attaches it to the serialized data. During deserialization, FTM checks the value to see if it is saved by the current process. If the two values are different, it will reject deserialization. The premise of this operation is that the attacker cannot guess or crack this value. Therefore, FTM will not unban any pointer that it thinks is incorrect. However, this threat model is useless when we can read any memory, so we have such a vulnerability.

FTM is implemented through the CStaticMarshaler class of combase. dll. In win7, It is the CFreeMalshaler class of ole32.dll. Take a look at the code of CstaticMarshaler: UnmarshallInterface, which is roughly as follows:

 

Note that this method will detect whether the secret value (secret) has been initialized, which can prevent attackers from using an uninitialized secret value (that is, 0 ). You need to use a safe character comparison function to avoid time-difference attacks against secret value checks. In fact, this is a non-backward porting fix. On win7, strings are compared using the repe cmdsd command, which is not a linear time comparison (* Note: Non-atomic operation ). Therefore, on Windows 7, you may be able to perform a time-difference bypass attack, although I think this is a huge expense.

The final structure looks like:

HRESULT CStaticMarshaler::UnmarshalInterface(IStream* pStm,                      REFIID riid,                       void** ppv) { DWORD mshlflags; BYTE  secret[16]; ULONGLONG pv; if (!CStaticMarshaler::_fSecretInit) {   return E_UNEXPECTED; } pStm->Read(&mshlflags, sizeof(mshlflags)); pStm->Read(&pv, sizeof(p)); pStm->Read(secret, sizeof(secret)); if (SecureCompareBuffer(secret, CStaticMarshaler::_SecretBlock)) {   *ppv = (void*)pv;   if ((mshlflags == MSHLFLAGS_TABLESTRONG)    || (mshlflags == MSHLFLAGS_TABLEWEAK)) {     ((IUnknown*)*ppv)->AddRef();   }   return S_OK; } else {   return E_UNEXPECTED; } }

To execute our code, we need to call the IMarshal interface in our com object. In particular, we need to execute two functions, Imarshal: getunexternalclass. When re-constructing the code, it will be used to return the CLSID of the COM object to be used. IMarshal: MarshalInterface, used to package appropriate pointer values for vulnerabilities. A simple example is as follows:

GUID CLSID_FreeThreadedMarshaller = { 0x0000033A, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, } };HRESULT STDMETHODCALLTYPE CFakeObject::GetUnmarshalClass(REFIID riid,void *pv,DWORD dwDestContext,void *pvDestContext,DWORD mshlflags,CLSID *pCid){memcpy(pCid, &CLSID_FreeThreadedMarshaller, sizeof(CLSID_FreeThreadedMarshaller));return S_OK;}HRESULT STDMETHODCALLTYPE CFakeObject::MarshalInterface(  IStream *pStm,  REFIID riid,  void *pv,  DWORD dwDestContext,  void *pvDestContext,  DWORD mshlflags){  pStm->Write(&_mshlflags, sizeof(_mshlflags), NULL);  pStm->Write(&_pv, sizeof(_pv), NULL);  pStm->Write(&_secret, sizeof(_secret), NULL);  return S_OK;}

It's easy enough. Let's see how to use it.

0x03 remove from sandbox

With the above background knowledge, it is time to leave the sandbox. To remove the code from the sandbox and execute it in the Agent process, there are three things we need to do:

Obtain the secret values of each FTM process in the mediation process.

Construct a false virtual table and a false object pointer.

Encapsulate an object to the proxy process for code execution.

Obtain the secret value of each process

This is very simple. We know that this value has a memory location, because the load address of combase. dll in the sandbox process and the Agent process is the same. Although ASLR is introduced after Vista, the system DLL is only randomized once after startup, so combase. dll will be loaded in the same place of each process. This is a weakness of ASLR in Windows, especially for Local Elevation of Privilege. However, if you read this value from a common IE operation, you will find a problem:

Unfortunately, FTM has not been initialized, which means we will not be able to use it any longer. How can we initialize it in the sandbox process? We only need to let the intermediary process do more COM operations, especially some operations that will introduce FTM.

You can use the open/save dialog box, which is implemented in Explorer Shell (shell32.dll). Of course, it uses COM. It is also a UI, so it will certainly use a STA, but will use a free-threaded object and eventually use FTM. So let's try it and open a dialog box to see the effect.

Good job. The reason for selecting this option is that you can use the IEShowSaveFileDialog API to start this dialog box in the sandbox process (this API is usually implemented by multiple proxies ). Obviously, this will display some UIS, but it is not important, because the FTM has been initialized during the dialog box display, and the user has no more to do.

Now we can hard encode some combase. dll offsets. Of course, you can also initialize FTM in the sandbox process dynamically and find its location through memory search.

Construct a false virtual table

The next challenge is to bring our fake virtual tables into the proxy process. Because we can read the memory of the proxy process, we can use the proxy process API to perform operations such as heap flooding. But is there a simpler way? The IE proxy process and the sandbox process have a shared memory section that they use to transmit settings and information. Some of these sections can be written to the sandbox process, so what we need to do is to find the ing of the corresponding intermediary process and then modify it to what we want. In this example, \ Sessions \ X \ BaseNamed \ Objects \ URLZones_user (X is the Session ID and user is the user name) is used, although it is mapped to the proxy process, sandbox programs can also be written, but some other things are needed.

We do not need to find this section brute force. We need to use PROCESS_QUERY_INFORMATION to open the process, and then use VirtualQueryEx to enumerate all mapped memory sections because it will return the size, therefore, we can quickly skip areas without ing. Then we can find a protection value for the write area (* Note: canary value, used to detect the buffer overflow value) to determine the release address.

DWORD_PTR FindSharedSection(LPBYTE section, HANDLE hProcess){  // No point starting at lowest value  LPBYTE curr = (LPBYTE)0x10000;  LPBYTE max = (LPBYTE)0x7FFF0000;  memcpy(&section[0], "ABCD", 4);  while (curr < max)  {    MEMORY_BASIC_INFORMATION basicInfo = { 0 };    if (VirtualQueryEx(hProcess, curr,               &basicInfo, sizeof(basicInfo)))    {       if ((basicInfo.State == MEM_COMMIT)          && (basicInfo.Type == MEM_MAPPED)          && (basicInfo.RegionSize == 4096))       {          CHAR buf[4] = { 0 };          SIZE_T read_len = 0;          ReadProcessMemory(hProcess, (LPBYTE)basicInfo.BaseAddress,                             buf, 4, &read_len);          if (memcmp(buf, "ABCD", 4) == 0)          {             return (DWORD_PTR)basicInfo.BaseAddress;          }        }        curr = (LPBYTE)basicInfo.BaseAddress + basicInfo.RegionSize;     }     else     {        break;     }  }  return 0;}

This determines where to create a virtual table and a false object in the shared memory. How can we call this virtual table? You may think of using a drop chain (* Note: Return orientation), but obviously we don't need to do this. Because all COM calls use stdcall, all parameters are placed on the stack, so we can call everything through the this pointer almost.

One attack method is to use a function similar to LoadLibraryW, and then construct a false object that will load the DLL pointing to the relative path. Because the virtual table pointer does not have any NULLCHAR (which makes it difficult to use this method in 64-bit systems), we can remove it (virtual table) from the path, this will cause it to load that database. To solve this problem, we can set the low 16 bits to any random value, and because the high 16 bits are not under our control, it will hardly end with 0, windows empty page Protection prohibits the allocation of 64 kB low addresses. Finally, our fake object looks like:

Of course, if you view the definition of its IUnknown interface, you will find that only AddRef and Release in the virtual table of this object have correct signature. If the proxy process calls QueryInterface on the object, signature is definitely incorrect at this time. In a 64-bit system, there is no problem because the parameter passing method is different. However, in a 32-bit system, this will cause the stack to be unable to be aligned. This is not the result I want. But it is actually okay. If this is a problem, there must be a solution, or simply call ExitProcess In the Agent process. However, when injecting an object, we need to select an appropriate method. If the object may not call it at all, this problem will not occur. This is what we will do next.

Send an object to the proxy process

In the end, it is also a simple point. Because all the interfaces of the proxy process used in the sandbox use COM, what we need to do is to find a pointer that only calls IUnknown, then, we will send our fake emails to the object. To achieve this purpose, I found that you can request the IEBrokerAttach interface of Shell Document View, which only has the following function prototype:

HRESULT AttachIEFrameToBroker (IUnknown * pFrame );

In order to make our pointer more perfect before it reaches the intermediary process, we will preset a frame. Therefore, calling this method without the pFrame object will immediately fail. Therefore, we do not need to worry that QueryInterface will be called, and our vulnerability code will be executed before the function is called, so we do not care about the problems caused by QueryInterface.

Therefore, we can call this method to create a false object. This causes COM to start sending the code to the OBJREF object. Finally, it stops at the other end of the IPC channel, that is, the place where the COM anti-mails are sent. This will call the FTM UnmarshalInterface method, and we have successfully found the secret value, so we can happily unpack our fake object pointer. In the end, this method will call AddRef on the object. In this case, we can also pass mshlflags to MSHLFLAGS_TABLESTRONG. This will call LoadLibraryW, and its "path" parameter is our dummy object, which will randomly load a DLL to the Agent process. All you need to do is play a calc, and now the task is complete.

In the end, a real service disconnection function will be called, but an error will be returned immediately. A beautiful sandbox jumps out, although it requires a lot of code to support.

0x04 End of speech

So I am in the original event tracking (https://code.google.com/p/google-security-research/issues/detail? A new PoC is added in id = 97), which can launch attacks on a 32-bit Windows 8.1 System (apparently you cannot patch your MS14-065 ). On 64-bit Windows 8.1, it does not run very well because the mediation process is 64-bit, although the Tab process may still be 32-bit. If you want him to run on 64-bit, you need to try again, but because you can control RIP, it is not too difficult. If you want to test on the latest machine, the PoC also has a tool, SetProcessDACL, which can modify the DACL of a program and add a new IE Compability SID with the read permission to it.

I hope this will give you some solutions to similar vulnerabilities. Also, don't complain about COM because it has nothing to do with it. This is just an example of how to break the layer-by-layer defense and evolve into arbitrary code execution and privilege escalation when the memory read is relatively harmless.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.