Google Android – Binder Generic ASLR Leak

  • 作者: Google Security Research
    日期: 2016-10-12
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/40515/
  • Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=889
    
    The interaction between the kernel /dev/binder and the usermode Parcel.cpp mean
    that when a binder object is passed as BINDER_TYPE_BINDER or BINDER_TYPE_WEAK_BINDER,
    a pointer to that object (in the server process) is leaked to the client process
    as the cookie value. This leads to a leak of a heap address in many of the privileged
    binder services, including system_server.
    
    See attached PoC, which leaks the addresses of allocated heap objects in system_server.
    
    Output running from the shell (run on droidfood userdebug build, MTC19X):
    
    shell@bullhead:/ $ /data/local/tmp/binder_info_leak
    --- binder info leak ---
    [0] opening /dev/binder
    [0] looking up activity
    0000: 00 . 01 . 00 . 00 . 1a . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 .
    0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 6f o 00 . 73 s 00 . 2e . 00 . 49 I 00 .
    0032: 53 S 00 . 65 e 00 . 72 r 00 . 76 v 00 . 69 i 00 . 63 c 00 . 65 e 00 . 4d M 00 .
    0048: 61 a 00 . 6e n 00 . 61 a 00 . 67 g 00 . 65 e 00 . 72 r 00 . 00 . 00 . 00 . 00 .
    0064: 08 . 00 . 00 . 00 . 61 a 00 . 63 c 00 . 74 t 00 . 69 i 00 . 76 v 00 . 69 i 00 .
    0080: 74 t 00 . 79 y 00 . 00 . 00 . 00 . 00 .
    BR_NOOP:
    BR_TRANSACTION_COMPLETE:
    BR_REPLY:
    target 0000000000000000cookie 0000000000000000code 00000000flags 00000000
    pid0uid 1000data 24offs 8
    0000: 85 . 2a * 68 h 73 s 7f . 01 . 00 . 00 . 01 . 00 . 00 . 00 . 55 U 00 . 00 . 00 .
    0016: 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
    - type 73682a85flags 0000017fptr 0000005500000001cookie 0000000000000000
    [0] got handle 00000001
    0000: 00 . 01 . 00 . 00 . 1c . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 .
    0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 61 a 00 . 70 p 00 . 70 p 00 . 2e . 00 .
    0032: 49 I 00 . 41 A 00 . 63 c 00 . 74 t 00 . 69 i 00 . 76 v 00 . 69 i 00 . 74 t 00 .
    0048: 79 y 00 . 4d M 00 . 61 a 00 . 6e n 00 . 61 a 00 . 67 g 00 . 65 e 00 . 72 r 00 .
    0064: 00 . 00 . 00 . 00 . 05 . 00 . 00 . 00 . 70 p 00 . 77 w 00 . 6e n 00 . 65 e 00 .
    0080: 64 d 00 . 00 . 00 .
    BR_NOOP:
    BR_TRANSACTION_COMPLETE:
    BR_REPLY:
    target 0000000000000000cookie 0000000000000000code 00000000flags 00000000
    pid0uid 1000data 28offs 8
    0000: 00 . 00 . 00 . 00 . 85 . 2a * 68 h 73 s 7f . 01 . 00 . 00 . 02 . 00 . 00 . 00 .
    0016: 7f . 00 . 00 . 00 . c0 . 19 . 9d . 8b . 7f . 00 . 00 . 00 .
    - type 73682a85flags 0000017fptr 0000007f00000002cookie 0000007f8b9d19c0
    [0] got handle 00000000
    
    
    Debugger output from system_server
    
    pwndbg> hexdump 0x0000007f8b9d19c0
    +0000 0x7f8b9d19c038 35 76 ab7f 00 00 0000 00 00 0000 00 00 00|85v.|....|....|....|
    +0010 0x7f8b9d19d065 00 6e 0074 00 5f 0040 d1 0c a87f 00 00 00|e.n.|t._.|@...|....|
    +0020 0x7f8b9d19e06a 16 20 0000 00 00 0020 ad 81 ab7f 00 00 00|j...|....|....|....|
    +0030 0x7f8b9d19f0e0 fc 7f 8e7f 00 00 00a0 f2 c7 8a7f 00 00 00|....|....|....|....|
    +0040 0x7f8b9d1a00
    
    This is pretty obviously the case; the code in Parcel.cpp that flattens binder objects
    to pass via binder transactions:
    
    status_t flatten_binder(const sp<ProcessState>& /*proc*/,
    const sp<IBinder>& binder, Parcel* out)
    {
    flat_binder_object obj;
    
    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
    IBinder *local = binder->localBinder();
    if (!local) {
    BpBinder *proxy = binder->remoteBinder();
    if (proxy == NULL) {
    ALOGE("null proxy");
    }
    const int32_t handle = proxy ? proxy->handle() : 0;
    obj.type = BINDER_TYPE_HANDLE;
    obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
    obj.handle = handle;
    obj.cookie = 0;
    } else {
    obj.type = BINDER_TYPE_BINDER;
    obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
    obj.cookie = reinterpret_cast<uintptr_t>(local); // <--- is a pointer to the object
    }
    } else {
    obj.type = BINDER_TYPE_BINDER;
    obj.binder = 0;
    obj.cookie = 0;
    }
    
    return finish_flatten_binder(binder, obj, out);
    }
    
    and the kernel code which processes this to send to the target process modifies
    the fp->handle entry, overwriting fp->binder, but does not alter fp->cookie, which
    contains the second pointer.
    
    case BINDER_TYPE_BINDER:
    case BINDER_TYPE_WEAK_BINDER: {
    struct binder_ref *ref;
    struct binder_node *node = binder_get_node(proc, fp->binder);
    if (node == NULL) {
    node = binder_new_node(proc, fp->binder, fp->cookie);
    if (node == NULL) {
    return_error = BR_FAILED_REPLY;
    goto err_binder_new_node_failed;
    }
    node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
    node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
    }
    if (fp->cookie != node->cookie) {
    binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
    proc->pid, thread->pid,
    (u64)fp->binder, node->debug_id,
    (u64)fp->cookie, (u64)node->cookie);
    goto err_binder_get_ref_for_node_failed;
    }
    if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
    return_error = BR_FAILED_REPLY;
    goto err_binder_get_ref_for_node_failed;
    }
    ref = binder_get_ref_for_node(target_proc, node);
    if (ref == NULL) {
    return_error = BR_FAILED_REPLY;
    goto err_binder_get_ref_for_node_failed;
    }
    if (fp->type == BINDER_TYPE_BINDER)
    fp->type = BINDER_TYPE_HANDLE;
    else
    fp->type = BINDER_TYPE_WEAK_HANDLE;
    fp->handle = ref->desc;
    binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
     &thread->todo);
    trace_binder_transaction_node_to_ref(t, node, ref);
    binder_debug(BINDER_DEBUG_TRANSACTION,
     "node %d u%016llx -> ref %d desc %d\n",
     node->debug_id, (u64)node->ptr,
     ref->debug_id, ref->desc);
    } break;
    
    In the case of 64-bit processes, we also leak the high dword of the fp->binder pointer, because 
    a uint32_t is smaller than a binder_uintptr_t.
    
    
    Proof of Concept:
    https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40515.zip