Monday, December 1, 2014

Internet Explorer EPM Sandbox Escape CVE-2014-6350

Posted by James Forshaw



This month Microsoft fixed 3 different Internet Explorer Enhanced Protected Mode (EPM) sandbox escapes which I disclosed in August. Sandboxes are one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 main areas of interest for Project Zero (and me in particular) as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are choke points for an attacker successfully exploiting a remote code execution vulnerability.

All three bugs are fixed in MS14-065, you can read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original reports here, here and here. CVE-2014-6350 is perhaps cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most interesting of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bunch, not because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug is particularly special but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 technique to exploit it to get code execution out of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox is unusual. It demonstrates a potential attack against DCOM hosts if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s an accompanying memory disclosure vulnerability. This blog post is going to go into a bit more detail about how you can exploit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerability.

What Was cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Vulnerability?

The vulnerability was due to weak permissions on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker process when IE is running in EPM mode. This didn’t actually affect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old Protected Mode (PM) for reasons I’ll soon explain. The EPM sandbox contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 untrusted tab processes which handle internet content, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker process acts a mediator providing privileged services to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 tabs when required. Interaction between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 tabs and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker uses a DCOM based IPC mechanism.

EPM Sandbox.png

Knowing how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Windows Access Check works we should be able to determine what permissions you’d receive if you tried to open cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker process from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 EPM sandbox. The access check used for code running in an AppContainer is slightly more complicated than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 normal Windows one. Instead of a single access check cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are two separate checks performed to calculate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 maximum granted set of permissions for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Discretionary Access Control List (DACL). The first check is done against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 normal user and group SIDs in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 token, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second is based on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 capabilities SIDs. The bitwise AND between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 two sets of permissions is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 maximum grantable permissions (we’re going to ignore deny ACEs as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y’re not relevant to this discussion).

AppContainer SID Checks (3).png

Now let’s take a look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DACL for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker process. A simplified form is shown in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 table below. The first pass of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 access check will match against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Current User SID which gives granted access of Full Control (show in Red). The second pass for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 capability will match cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IE Capability SID (show in Blue), once combined togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 maximum permissions is Read Memory, Query Information. The fact that we can get Read Memory permissions is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerability which Microsoft fixed.

User
Permissions
S-1-15-3-4096 (IE Capability SID)
Read Memory, Query Information
Current User
Full Control
SYSTEM
Full Control

We can call OpenProcess passing it cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PID of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 desired access of PROCESS_VM_READ and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel will return cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandboxed process an appropriate handle. With this handle it’s trivial to read arbitrary memory from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ReadProcessMemory API. This even correctly handles invalid memory addresses so nothing should crash unexpectedly.

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

Things get a bit more complicated if you’re on 64-bit Windows and trying to exploit from a 32-bit tab process, Wow64 gets in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way. You can’t directly use ReadProcessMemory to read memory from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 64-bit broker. You can use something like wow64ext to get around this limitation, but for now we’ll just ignore it.

But wait, what about PM, why isn’t cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re as well? In PM only cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 single access check is performed so we should get Full Control but we don’t due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Mandatory Integrity Label (IL) feature introduced in Windows Vista. When a process tries to open anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 access check in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel will first compare cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IL of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calling process against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value specified in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target process’ System ACL (SACL). If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calling process’ IL is lower than that specified by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target process cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 maximum access is limited to a small subset of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 available access permissions (such as PROCESS_QUERY_LIMITED_INFORMATION). This will block PROCESS_VM_READ or anything more dangerous even before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DACL is checked.

Okay so let’s take a look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 token for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 EPM sandboxed process in Process Explorer, we can clearly see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 token has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Low Mandatory Level (highlighted in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 below screenshot).

ie_low_integrity.PNG

Curiously cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AppContainer access check seems to ignore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IL at least for any resource with a Medium (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 default) and below level. If a resource passed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DACL check cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n those permissions are granted regardless of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IL. This seems to work for any securable resource including files and registry keys. I don’t know if this is by design but it seems like a weakness, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IL was being checked this issue would have never existed.

Exploiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Vulnerability

The original PoC supplied in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issue tracker exploited a method in one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker IPC interfaces to read arbitrary files on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system. By reading a per-process HMAC key cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PoC could forge a valid token and call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 appropriate method (CShDocVwBroker::GetFileHandle) to open cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file. This is useful for EPM because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AppContainer prevents reading arbitrary files. Still this is only a read, not a write. Ideally we would like to be able to completely escape cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox, not just disclose cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of files.

This might initially seem like a difficult task, but it turns out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are more technologies which use per-process secrets to make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365mselves secure. One such technology is my all-time favourite Windows technology, COM (I might be joking when I say that). Turns out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s a way of getting code execution in many application which implement remote COM services, as long as we’re able to disclose cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 content of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hosting process.

COM Threading Models, Apartments and Interface Marshaling

COM is used by many different components in Windows from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Explorer Shell to local privileges services such as BITS. Each use case has different requirements and restrictions, for example UI code needs all code to run on a single thread ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OS will get unhappy. On cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r hand a utility class might be completely thread safe. To support cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se requirements COM supports a couple of threading models which relieves some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 burden on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 programmer.

An object is contained within an Apartment which defines how methods on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object can be called. There are two types of Apartments, Single Threaded Apartment (STA) and Multi Threaded Apartment (MTA). When considering how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se apartments interact with how methods are called we need to define cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relationship between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object. For that we’ll define cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller of methods as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Client and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Server.

The Client’s Apartment is determined by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flag passed to CoInitializeEx (we default to STA if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “legacy” CoInitialize is called instead). The Server’s apartment depends on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM object threading model definition in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Windows registry. This can be one of three settings, Free (means multi-threaded), Apartment (means single-threaded) and Both. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Client and Server have compatible apartments (which really only occurs when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server object is registered as supporting both threading models). cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n calls made to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object are direct function pointer dereferences via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object’s virtual function table. However in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case of STA calling MTA or MTA calling STA we need to proxy cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calls in someway, COM does this through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process of Marshaling. We can summarise this in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following table.

Client
Server
Inter-object communication via:
STA
Free
Marshaling, unless server implements cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 free-threaded marshaler and is in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same process
MTA
Apartment
Marshaling
STA
Both
Direct Access
MTA
Both
Direct access

Marshaling refers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process of serializing method calls to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Server object. This is especially important in STA as all methods must be called on a single thread. This is typically coordinated using a Windows message loop, in fact if your application has no windows or message loop it will create cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m for you if you create a STA. When a Client calls an object in an incompatible apartment it really calls a special proxy object. This proxy knows about each different COM interface and method, including what parameters each method takes.

The proxy takes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parameters, serializes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 information using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 built-in COM marshaling code and packages cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m up to be sent to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server. At cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server side a dispatcher unmarshals cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parameters and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n invokes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 appropriate method on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Server object. Any return values are sent back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same way.

COM Marshaling.png
It turns out this model works equally well in-process as it does between processes using DCOM. The same Marshaling techniques of proxies and dispatcher works between processes or computers. The only difference is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 transport for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 marshaled parameters, instead of in-memory for a single process it might use local RPC, named pipes or even TCP depending on where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Client and Server are located.

The Free-Threaded Marshaler

Okay so how’s this going to help in exploiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory disclosure vulnerability? To understand I need to describe something called cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Free-Threaded Marshaler (FTM). This is referred to in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous table when a STA Client calls a method on a multi-threading capable Server. It seems awfully wasteful that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Client needs to go through this whole proxing/marshaling effort. Can’t it just call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object directly? This is what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM solves.

When a COM object is instantiated in an incompatible apartment a reference to that object must be passed back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller. This is achieved using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same marshaling operations as during a call. In fact this same mechanism is used when a call is made to a object method which takes COM object parameters. The mechanism cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 marshaler uses to pass this reference is to build a special data stream called an OBJREF. This stream contains all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 information a Client needs to construct a proxy object and contact cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Server. This implements a pass-by-reference semantic for COM objects. An example of an OBJREF is shown below:

objref.PNG

In some scenarios though it makes sense to pass an object by-value, for example this would eliminate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 proxy. For that purpose cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OBJREF stream can also use pass-by-value semantics where all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data needed to reconstruct cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original object in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Client’s apartment is specified. When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unmarshaler reconstructs cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object, instead of getting a proxy it creates and initializes a brand new copy of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original object. An object can implement it’s own pass-by-value semantics by implementing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IMarshal interface.

This feature is used by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM to “cheat” cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system. Instead of passing across cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original object’s data, it instead just passes a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original object in memory serialized in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OBJREF. When unmarshaled this pointer is deserialized and returned to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller. It acts as a fake-proxy and effectively just allows direct calls to be made on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original object.

Free-Threading COM Marshaling.png

Now if at this point you might be getting uncomfortable that’s understandable. As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 marshaler is little different between DCOM and in-process COM this is surely a massive security hole? Fortunately not, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM doesn’t just send cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer value it also tries to ensure only cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same process which marshaled cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer can unmarshal it again. It does this by generating a per-process 16 byte random value which is attached to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 serialized data. When deserializing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM checks that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value matches cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 one in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current process, rejecting anything which is incorrect. The assumption here is an attacker can’t guess or brute-force such a value, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM will never unmarshal an invalid pointer. But this threat model obviously doesn’t take into account being able to read process memory, and it just so happens we have just such a vulnerability.

The implementation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM lies in combase.dll specifically cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CStaticMarshaler class. For Windows 7 it’s in ole32.dll and is called CFreeMarshaler instead. Looking at CStaticMarshaler::UnmarshalInterface we have code which is roughly as follows:

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

Note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method checks that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 secret is initialized first, this prevents accidentally using a zero-secret value if it was uninitialized. Also note cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 use of a secure comparison function to combat timing attacks against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 secret check. Actually this is a case of not-back porting fixes. In Windows 7 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 comparison uses a repe cmdsd instruction, which isn’t constant time. Therefore on Windows 7 you might be able to exploit this check by mounting a side-channel timing attack, although I think it would be pretty complex and time consuming to do so.

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end our structure looks like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following:
FTM Data Structure.png
In order to exploit this in our code we need to implement cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IMarshal interface on our COM object. Specifically we need to implement two methods, IMarshal::GetUnmarshalClass which returns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CLSID of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM object to use when reconstructing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code and IMarshal:MarshalInterface which will package up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 appropriate pointer value for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit. A simple example is shown below:

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

Simple enough. We’ll get to how this is used later.

Escaping cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Sandbox


With cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 background out of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way it’s time to escape cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox. There are three things we need to do to get code execution from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker process:
  1. Extract cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM per-process secret from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker.
  2. Construct a fake v-table and a fake object pointer.
  3. Marshal an object into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker to get code executed.

Extract Per-Process Secret

This should be a pretty easy task, we know where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 secret’s held in memory as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 load address of combase.dll is going to be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox process as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker process. Even though Windows Vista introduced ASLR system DLLs are only randomised once at boot, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore combase.dll is going to be mapped to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same location in every process. This is a weakness in ASLR on Windows, especially for local privilege escalation .But if you dump cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 values during normal IE operation you’ll see a problem:

windbg_ie_ftm.PNG
Unfortunately cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM isn’t initialized, which means that we couldn’t exploit this even if we wanted to. So how are we going to get it to initialize from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandboxed process? We just need to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker to do more COM stuff, specifically something which is likely to invoke cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM.
For that we can use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file open/save dialog. This dialog actually hosts cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Explorer Shell (well really shell32.dll) which uses COM under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hood. As it’s also a UI cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n it will almost certainly use an STA but could call into Free Threaded objects which would invoke cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM. So lets just try and open cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dialog manually and see.

windbg_ie_ftm_init.PNG
Much better. The real reason to choose this is we can open cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dialog from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandboxed process using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IEShowSaveFileDialog API call (which is actually implemented by various broker calls). Obviously this will display some UI but it doesn’t really matter, by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dialog is displayed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM is already initialized, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re isn’t anything cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user could do about it.

For now we’ll just hard code cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 offsets into combase.dll. But of course you could find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m dynamically by initializing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandboxed process and finding cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 offset through a memory search for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 marshaled secret.

Constructing a Fake V-Table

Now cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next challenge is to get our fake v-table into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker process. As we can read out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker’s memory we could certainly do something like heap flooding using one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker APIs, but is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re an easier way? The IE broker and sandboxed processes share a few memory sections to pass settings and information between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365mselves. Some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se sections are writable by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandboxed process, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore all we need to do is find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding mapping in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n modify to our heart’s content. In this case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 section \Sessions\X\BaseNamedObjects\URLZones_user was chosen (where X is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 session ID and user is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 username), but anything would do as long as it’s already mapped into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker and writable by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandboxed process.

We don’t have to do much in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way of brute-forcing to find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 section. As we can open cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process with PROCESS_QUERY_INFORMATION access we can call VirtualQueryEx to enumerate mapped memory sections. As it returns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size we can quickly skip unmapped areas. Then we can look for a canary value we wrote to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 section to determine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exact location.

DWORD_PTR FindSharedSection(LPBYTE section, HANDLE hProcess)
{
  // No point starting at lowest value
  LPBYTE curr = (LPBYTE)0x10000;
  LPBYTE max = (LPBYTE)0x7FFF0000;

  memcpy(§ion[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;
}

Once we’ve determined cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 location of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared memory section we need to build cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 v-table and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake object. What should we call through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 v-table? You might think at this point it’s time to build a ROP chain, but of course we don’t really need to do that. As all COM calls use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stdcall calling convention where all arguments are placed on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack we can call any location we like with 1 argument we almost completely control, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 this pointer to our fake object.

One way of exploiting this is to use a function such as LoadLibraryW and construct cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake object with a relative path to a DLL to load. As long as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 v-table pointer doesn’t contain any NULs (which makes this technique less useful on 64-bit I might add) we can remove it from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path and cause it to load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 library. We can set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lower 16 bits to any arbitrary value we like to eliminate this problem and while we don’t control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 upper 16 bits cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s effectively no chance it would end up as a 0 due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NULL page protection in Windows preventing allocations below cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 64KiB point. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end our fake object looks like:

Fake VTable.png

Of course if you look up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 definition of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IUnknown interface which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 V-Table implements only AddRef and Release have a compatible signature. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker calls QueryInterface on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signature isn’t correct. On 64-bit this wouldn’t matter due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way parameters are passed but on 32-bit this will cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack to be misaligned, not ideal. But it doesn’t really matter, we could always fix this up if it’s a problem or just call ExitProcess from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker, still if we choose an appropriate method when injecting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object it might never call it at all, which is what we’ll do here.

Marshaling an Object into Broker

Finally cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 easy bit, as pretty much all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 interfaces to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox use COM all we need to do is find a call which takes a bare IUnknown pointer and pass it our fake marshaling object. For this purpose I found that you could query for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IEBrokerAttach interface from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Shell Document View broker which has a single function with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following prototype:

HRESULT AttachIEFrameToBroker(IUnknown* pFrame);

To make this even better before we get hold of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 frame has already been set, this makes this method fail immediately without touching cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pFrame object. Therefore we don’t need to worry about QueryInterface being called. Our exploit is going to
run before this function ever gets called so we don’t really care.

So we create our fake object and call this method. This will cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM infrastructure to marshal our data into an OBJREF. This ends up on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r side of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IPC channel where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM infrastructure will unmarshal it. This causes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FTM UnmarshalInterface method to be called, and as we’ve successfully discovered cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 secret value will happily unpack our fake object pointer. Finally cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method will call AddRef on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object as we can set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 passed mshlflags to MSHLFLAGS_TABLESTRONG. This will execute LoadLibraryW with our fake object as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path parameter. This’ll load an arbitrary DLL into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker, all that’s required is to pop calc and it’s job done.

Marshal.png
Finally cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 real server function will be called, but that returns immediately with an error. Nice clean sandbox escape, even if it requires a fair amount of actual code to achieve.

Wrapping it Up

So I’ve added a new PoC to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original issue for this bug to demonstrate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack on 32-bit Windows 8.1 update (obviously without cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MS14-065 patch). It won’t work directly on 64-bit Windows 8.1 as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 broker process runs as 64-bit even if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 tab processes might be 32-bit. You’ll need to be a bit more creative to get it to work on 64-bit, but you can easily get control over RIP so it isn’t a major concern. If you want to test it on an up to date machine, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PoC contains a tool, SetProcessDACL, which modifies a process’s DACL to re-add read permissions for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IE Capability SID.

Hopefully it gives you an idea on how you could exploit similar bugs. Still, let’s not blame COM for this, since it isn’t really its fault. This is only a demonstration of how a relatively innocuous issue, memory disclosure in a privileged process, completely breaks many security assumptions leading to code execution and elevation of privilege.

1 comment: