<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>

    剖析臟牛4_madvise()與漏洞成因

    VSole2021-11-04 17:03:21

    測試程序

    int fd;struct stat st;void *mem;
    void processMem(void){    int f = open("/proc/self/mem", O_RDWR);    lseek(f, mem, SEEK_SET);    write(f, "BBB", 3);
        printf("%s", (char*)mem);
        madvise(mem ,100 ,MADV_DONTNEED);}
    int main(void){    fd = open("./test", O_RDONLY);    fstat(fd, &st);    mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
        processMem();}
    

    sys_madvise()

    sys_madvise()首先進行簡單的參數處理

    如果需要的話獲取mmap_sem信號量, 然后遍歷[start, start+len)內所有的VMA, 對于每個VMA調用madvise_vma()進行處理. 這里我們只關注behavior = MADV_DONTNEED

    madvise_vma()

    madvice_vma()根據behavior把請求分配到對應處理函數, 對于MADV_DONTNEED會調用madvise_dontneed()處理

    madvise_dontneed()

    排除掉一些無法丟棄的情況后, 會調用zap_page_range()處理

    zap_page_range()

    zap_page_range()會遍歷給定范圍內所有的VMA, 對每一個VMA調用unmap_single_vma(…)

    后續會沿著unmap_single_vma() => unmap_page_range() => zap_pud_range() => zap_pmd_range() => zap_pte_range()的路徑遍歷各級頁表項, 最后調用zap_pte_range()遍歷每一個PTE

    zap_pte_range()

    zap_pte_range()會釋放范圍內所有的頁, 函數頭如下

    然后遍歷范圍內所有頁, 清空頁表中對應的PTE, 并減少對應頁的引用計數, 當頁的引用計數為0時會被內核回收

    dirty_COW漏洞

    回想一下利用/proc/self/mem寫入進程只讀內存區的過程: access_remote_vm()會先調用get_user_pages()鎖定要寫入的頁, get_user_pages()會通過while( !follow_page_mask(foll_flag) ){ faultin_page(foll_flag); } 這個循環分配滿足foll_flag要求的頁

     __get_user_pages()第一次循環

    faultin_page()判斷屬于寫入只讀區域的情況, 因此會調用do_cow_fault()

    do_cow_fault()會復制原始的文件緩存頁到一個新頁中, 并設置PTE映射到這個新頁, 但由于VMA不可寫入, 因此這個新頁的PTE頁沒有設置RW標志

    __get_user_pages()第二次循環

    由于foll_flags中有FOLL_WRITE標志, 但是頁對應的PTE沒有RW標志, 因此follow_page_mask()判斷權限有問題, 再次進入faultin_page()

    faultin_page()判斷, 屬于寫入只讀的已存在的頁造成的問題, 因此會調用do_wp_page()處理

    do_wp_page()發現對應頁是只有一個引用的匿名頁,因此會調用wp_page_reuse()直接重用這個頁

    wp_page_reuse()由于對應VMA只讀, 因此只會給PTE設置一個Dirty標志, 而不會設置RW標志, 然后返回一個VM_FAULT_WRITE表示內核可以寫入這個頁

    返回到faultin_page()中, 由于handle_mm_fault()返回了VM_FAULT_WRITE, 因此會去掉FOLL_WRITE標志, 含義為: 雖然此頁對應PTE不可寫入, 但是已經COW過了, 內核是可以寫入的, 后續follow_page_mask()就不要檢查能不能寫入了

    如果說在清除FOLL_WRITE標志之后, 第三次調用follow_page_mask()之前, 我們通過madivse()設置此頁對應PTE為空會發生什么?

    首先follow_page_mask()會因為對應PTE為NULL而再次失敗, 進入faultin_page(), 但是注意, 這次進入的時候沒有FOLL_WRITE標志

    faultin_page()因此設置fault_flags時是沒有FAULT_FALG_WRITE標志的, 也就是說faultin_page()對handle_mm_fault()承諾不會寫入這個頁

    handle_mm_fault()由于pte為none, 并且不要求寫入, 因此最終會分派給do_read_fault()處理

    do_read_fault()會查找這片VMA映射的地址空間中, address對應的原始緩存頁, 然后返回這個原始緩存頁

    如果是用戶映射一片只讀內存頁到文件, 返回原始緩存頁是沒有問題的, 因為用戶無權對其進行寫入. 但是在這里access_remote_vm()后續會調用copy_to_user_page() 寫入__get_user_pages()鎖定的頁, 由此污染了文件的原始緩存頁.

    一段時間后當進行磁盤同步時(sync, kflushd….)內核會把被污染的頁面回寫到磁盤中, 從而寫入特權文件完成攻擊

    那么下一個問題這個條件競爭的時間窗口有多大? 由于faultin_page()返回之后會調用cond_resched()切換到別的任務, 因此時間窗口是非常大的

    受攻擊時對/proc/self/mem進行寫入時的執行流程:

    EXP偽代碼

    Main:    fd = open(filename, O_RDONLY)        //打開一個文件    fstat(fd, &st)    map = mmap(NULL, st.st_size , PROT_READ, MAP_PRIVATE, fd, 0)    //把文件映射到map指向的內存區域    start Thread1    start Thread2
    Thread1:    f = open("/proc/self/mem", O_RDWR)    //打開mem文件    while (1):        lseek(f, map, SEEK_SET)    //偏移到map映射的區域        write(f, shellcode, strlen(shellcode))    //寫入
    Thread2:    while (1):        madvise(map, 100, MADV_DONTNEED)    //取消映射
    

    反思

    對于進程中的只讀內存區域, 如果通過地址進行寫入會得到一個段錯誤, 但是通過mem文件進行寫入, 就會得到一個dirty的COW的只讀頁, 為什么會有這樣的差異?

    對于段錯誤, 這個很好理解, 但是通過mem文件寫入一個進程的只讀內存區, 破壞了進程的地址空間. 那么為什么內核還要引入這種外部訪問機制呢? 這是為了方便調試器和一些跟蹤程序而加入的設計

    這個漏洞的修復也很簡單, COW之后不去掉FOLL_WRITE標志, 而引入一個新的標志FOLL_COW.

    • pte設置了RW標志, 表示頁可寫入
    • flags設置了FOLL_COW標志, 表示這是一個COW之后的頁面, 雖然PTE說不可寫入, 但是內核實際可以寫入
    • 這樣就算進入follow_page_mask()前這個PTE被設為nonoe ,但foll_flags保留了FOLL_WRITE標志, 仍然會要求faultin_page分配一個要寫入的頁.
    • 在follow_page_mask()檢查的時候如果foll_flags中設置了FOLL_WRITE要求寫入, 那么下面兩種情況都會被判斷為頁可寫入
    • 修復的diff如下
    diff --git a/include/linux/mm.h b/include/linux/mm.hindex e9caec6..ed85879 100644--- a/include/linux/mm.h+++ b/include/linux/mm.h@@ -2232,6 +2232,7 @@ static inline struct page *follow_page(struct vm_area_struct *vma, #define FOLL_TRIED    0x800    /* a retry, previous pass started an IO */ #define FOLL_MLOCK    0x1000    /* lock present pages */ #define FOLL_REMOTE    0x2000    /* we are working on non-current tsk/mm */+#define FOLL_COW    0x4000    /* internal GUP flag */
     typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr,             void *data);diff --git a/mm/gup.c b/mm/gup.cindex 96b2b2f..22cc22e 100644--- a/mm/gup.c+++ b/mm/gup.c@@ -60,6 +60,16 @@ static int follow_pfn_pte(struct vm_area_struct *vma, unsigned long address,     return -EEXIST; }
    +/*+ * FOLL_FORCE can write to even unwritable pte's, but only+ * after we've gone through a COW cycle and they are dirty.+ */+static inline bool can_follow_write_pte(pte_t pte, unsigned int flags)+{+    return pte_write(pte) ||+        ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte));+}+ static struct page *follow_page_pte(struct vm_area_struct *vma,         unsigned long address, pmd_t *pmd, unsigned int flags) {@@ -95,7 +105,7 @@ retry:     }     if ((flags & FOLL_NUMA) && pte_protnone(pte))         goto no_page;-    if ((flags & FOLL_WRITE) && !pte_write(pte)) {+    if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) {         pte_unmap_unlock(ptep, ptl);         return NULL;     }@@ -412,7 +422,7 @@ static int faultin_page(struct task_struct *tsk, struct vm_area_struct *vma,      * reCOWed by userspace write).      */     if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))-        *flags &= ~FOLL_WRITE;+            *flags |= FOLL_COW;     return 0; }
    
    pteflags
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    希望能通過這篇文章讓大家對最近qemu逃逸題目學習有一點幫助。
    NX實現機制淺析
    2021-10-12 16:53:04
    是否開啟NX取決于參數-z設置,而gcc僅僅是將-z keyword傳遞給linker——ld,并不會真正解析該參數:
    【經典回顧系列】 Windows SMB Ghost CVE-2020-0796漏洞分析與利用(三)
    (由于PTE控制著4KB物理頁的屬性,因此目標代碼所屬的整個物理頁都被設置為不可執行。若是,則修改PTE的執行屬性并進行事件注入至 #DB,內核異常處理函數將會將該異常派發給調試器。若不是,則仍需要修復PTE的可執行屬性,置位rflags.TF以便于下條指令觸發 #DB 異常被vmm接管,修復cr2并進行事件注入 #PF。
    上?篇?章中已經簡單介紹過了CET的基本原理和實際應?的?些技術,站在防守?的視?下,CET確實是?個能 ?較有效防御ROP攻擊技術的措施。那么在攻擊者的視?來看,研究清楚CET的技術細節,進?判斷CET是否是? 個完美的防御?案,還是存在?定的局限性,則是攻擊?的重中之重。 本?由淺?深地講述CET的實現細節,最后提出?個理論可?的繞過?案,供研究者參考。
    00 前言在 HWS2021 入營選拔比賽的時候,遇到了一道 QEMU 逃逸的題目,那個時候就直接莽上去分析了一通,東拼西湊的把 EXP 寫了出來。但是 QEMU 逃逸這部分的內容實在是比較復雜,而且涉及到了很多我完全沒有了解過的知識,所以一直鴿到了現在。System mode:系統模式,在這種模式下,QEMU 可以模擬出一個完整的計算機系統。
    美國商務部工業安全局將四家公司列入出口控制的實體名單:以色列公司 NSO Group 和 Candiru,俄羅斯公司 Positive Technologies 和新加坡公司 Computer Security Initiative Consultancy PTE. LTD.。
    美國制裁了四家開發監視惡意軟件或銷售民族國家行為者(包括 NSO Group)使用的黑客工具的公司。
    本月初曝光的谷歌Chrome zero day漏洞已被積極利用,但現已修復。“我們認為這些襲擊具有高度的針對性”。在黎巴嫩發現的感染序列始于攻擊者破壞一家新聞機構員工使用的網站,該網站用于從演員控制的域注入惡意JavaScript代碼,該域負責將潛在受害者重定向到攻擊服務器。Avast評估了收集的信息,以確保漏洞僅被交付給預期目標。
    OOB技術通常需要易受攻擊的實體生成出站TCP/UDP/ICMP請求,然后允許攻擊者泄露數據。OOB攻擊的成功基于出口防火墻規則,即是否允許來自易受攻擊的系統和外圍防火墻的出站請求。而從域名服務器中提取數據,則被認為是最隱蔽有效的方法。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类