IT技術互動交流平jiao)/h4>

大福彩票官网

發布日期︰2020-02-18 11:11:39

大福彩票官网

Linux系統中,用戶(hu)態(tai)的(de)棧空間通常大約是8MB。如果有程序發生了棧溢出(chu)的(de)話(比如無(wu)限遞歸),棧所在的(de)內存保(bao)護(hu)頁(ye)一般會捕捉到。

Linux內核棧(可以用來處(chu)理系統調用)和用戶(hu)態(tai)的(de)棧huan)懿灰謊D諍甦幌嘍岳此蹈duan)︰32位x86架構平jiao)ㄎ096byte , 64位系統則有16384byte(內核棧大小(xiao)由(you)THREAD_SIZE_ORDER 和 THREAD_SIZE 確fan)  K鞘怯you)內核的(de)伙伴內存分配器(qi)分配,伙伴內存分配器(qi)是內核常用來分配頁(ye)大小(xiao)(以及頁(ye)大小(xiao)倍數)內存的(de)分配器(qi),它不創建內存保(bao)護(hu)頁(ye)。也就是說,如果內核棧溢出(chu)的(de)話,它將直接覆蓋正常的(de)數據。正因如此,內核代碼必須(xu)(通常也是)在棧上分配大內存的(de)時(shi)候非qian)Pxiao)心,並且必須(xu)阻止過多的(de)遞歸。

Linux上的(de)大多數文件系統既不用底(di)層設備(偽文件系統,比如sysfs, procfs, tmpfs等),也不用mei)檣璞福ㄒ話閌怯ying)盤上的(de)一塊)作為備用存儲設備。然(ran)而, ecryptfs 和overlayfs是例(li)外。這兩者是堆疊的(de)文件系統,這種(zhong)文件系統會使用其他文件系統上的(de)文件夾作為備用存儲設備(overlayfs則使用多個不同(tong)文件系統上的(de)多個文件作為備用存儲設備)。被(bei)用作備用存儲設備的(de)文件系統稱為底(di)層文件系統,其上的(de)文件稱為底(di)層文件。這種(zhong)層疊文件系統的(de)特點是它或多或少的(de)會訪問底(di)層文件系統,並對訪問的(de)數據做(zuo)一些修改。 Overlayfs融(rong)合(he)多個文件系統,ecryptfs則進行了相應的(de)加密。

層疊文件系統實際上存在潛在風險,因為其訪問虛擬文件系統的(de)函數常會訪問到底(di)層文件系統的(de)函數,相較直接訪問底(di)層文件系統的(de)句柄(bing),這會增(zeng)大棧空間。考慮這樣一個場景(jing)︰如果用層疊文件系統作為另外一個層疊系統的(de)備用存儲設備,由(you)于每(mei)一層文件系統的(de)堆疊都增(zeng)大了棧空間,內核棧就會在某(mou)些情況(kuang)下溢出(chu)。但是,設置FILESYSTEM_MAX_STACK_DEPTH 限制文件系統的(de)層數,只允(yun)許最多兩層層疊文件系統放在非層疊文件系統上,就可以yuan) 庹飧鑫侍狻/p>

在Procfs偽文件系統上,系統中運(yun)行的(de)每(mei)一個進程都有一個文件夾,每(mei)個文件夾包含一些描述該進程的(de)文件。值得注意(yi)的(de)是每(mei)個進程的(de)“mem”,“ environ”和“cmdline”文件,因為訪問這些文件會同(tong)步訪問目標(biao)進程的(de)虛擬內存。這些文件顯yun)玖瞬煌tong)的(de)虛擬內存地址範(fan)圍︰

 

1.“mem”文件顯yun)玖甦魴檳餑詿嫻?販fan)圍(需要(yao)PTRACE_MODE_ATTACH 權限)

2.“environ”文件顯yun)玖m->env_start 到mm->env_end的(de)內存範(fan)圍(需要(yao)PTRACE_MODE_READ權限)

3.“cmdline”文件顯yun)玖m->arg_start 到mm->arg_end的(de)地址範(fan)圍(如果mm->arg_end的(de)前一個字(zi)符是null 的(de)話)

如果可以用mmap()函數映射“mem”文件的(de)話(啥(sha)意(yi)義也沒有,別想太(tai)多),就可以映射成如下圖所示的(de)樣子︰

 

word-wrap: break-word; word-break: break-all; font-size: 8pt;">

接下來,假設/proc/$pid/mem的(de)映射有一些錯誤(wu),那麼在進程C里的(de)內存讀取錯誤(wu),將會導致從進程B中映射的(de)內存出(chu)錯,進而導致進程B里出(chu)現其它的(de)內存錯誤(wu),進而導致從A進程映射的(de)內存出(chu)錯,這就是一個遞歸內存錯誤(wu)。

可是,現實中這是不可行的(de),“mem”,“environ”,“cmdline ”文件只能用VFS函數讀寫,mmap無(wu)法(fa)使用︰

 

staticconst struct file_operations proc_pid_cmdline_ops = {

 .read   = proc_pid_cmdline_read,

 .llseek = generic_file_llseek,

};

[...]

staticconst struct file_operations proc_mem_operations = {

 .llseek  = mem_lseek,

 .read    = mem_read,

 .write   = mem_write,

 .open    = mem_open,

 .release = mem_release,

};

[...]

staticconst struct file_operations proc_environ_operations = {

 .open    = environ_open,

 .read    = environ_read,

 .llseek  = generic_file_llseek,

 .release = mem_release,

};

 

相關ecryptfs文件系統,比較有趣的(de)一個細節在于它支持mmap()。用戶(hu)看到的(de)內存映射必須(xu)是解(jie)密的(de),而底(di)層文件系統的(de)內存映射是加密的(de),因而ecryptfs 文件系統不能將mmap()函數直接映射到底(di)層文件系統的(de)mmap()函數上。Ecrypt 文件系統在內存映射時(shi)使用了自己的(de)頁(ye)緩(huan)存。

ecryptfs文件系統處(chu)理頁(ye)錯誤(wu)的(de)時(shi)候,必須(xu)以yue)持zhong)方式讀取底(di)層文件系統上加密的(de)頁(ye)。這可以通過讀取底(di)層文件文件系統的(de)頁(ye)緩(huan)存(使用底(di)層文件系統的(de)mmap函數)來ci)迪鄭  欽庋冉舷xiao)耗內存。于是它直接使用底(di)層文件系統的(de) VFS讀取函數(通過kernel_read()),這樣做(zuo)更加直接有效,但是這個做(zuo)法(fa)有副作用,就是有可能會mmap() 到通常不能映射的(de)解(jie)密後的(de)文件(因為只要(yao)底(di)層文件有讀權限並且包含合(he)法(fa)的(de)加密數據, ecryptfs文件系統的(de)mmap函數就能工作)。

大福彩票官网

在此,我們就能描繪完整的(de)攻(gong)擊方式了。首先創建一個進程A,進程號(hao)為$A。然(ran)後創建一個ecrypptfs 掛載/tmp/$A,使/proc/$A作為它的(de)底(di)層文件系統(ecryptfs 應該只有一個 key,這樣文件名才不會被(bei)加密)。現在,如果/proc/$A下相應的(de)文件有合(he)法(fa)的(de)ecryptfs 文件頭的(de)話,那麼 /tmp/$A/mem, /tmp/$A/environ 和 /tmp/$A/cmdline就可以yuan)揮成洹3怯root 權限,否(fu)則無(wu)法(fa)將內存映射到進程 A的(de)0×0處(chu),也就是 /proc/$A/mem 的(de)開頭。因此從開始讀取 /proc/$/A 總是會返回-EIO,而且 /proc/$A/mem 不會有一個合(he)法(fa)的(de) ecryptfs 文件頭。如此,environ 和 cmdline 文件才有攻(gong)擊的(de)可能性。

在使用CONFIG_CHECKPOINT_RESTORE編(bian)譯的(de)內核(至少是Ubuntu的(de) distro 內核)中,非特權用戶(hu)可以通過prctl(PR_SET_MM, PR_SET_MM_MAP, &mm_map,sizeof(mm_map), 0)設置mm_struct 中的(de) arg_start, arg_end, env_start 和 env_end值。這使得映射 /proc/$A/environ 和 /proc/$A/cmdline到任(ren)意(yi)虛擬內存範(fan)圍成為可能。(不支持checkpoint-restore的(de)內核中,攻(gong)擊過程就稍微(wei)有點麻(ma)煩,但使用所需的(de)參(can)數區域和環境變(bian)量的(de)mou)chang)度重(zhong)新執(zhi)行,然(ran)後取代部分棧空間的(de)映射,還是有可能的(de)。)

如果一個有效加密的(de)ecryptfs文件被(bei)加載到進程A的(de)內存中,並且它的(de)環境變(bian)量也被(bei)配置為指向這塊區域,那麼環境變(bian)量區域里的(de)解(jie)密形式的(de)數據就可以在 /tmp/$A/environ文件中獲取。這個文件也可以yuan)揮成淶澆的(de)內存中xiao)N 四芄gou)重(zhong)復該進程,某(mou)些數據需要(yao)反復加密,進而創建一個加密的(de)matroska 文件,並將這個文件加載到進程 A的(de)內存中xiao)U庋煥矗 成浠?嘟探jie)密環境變(bian)量區域的(de)進程鏈(lian)就建立起來了︰

 

 

 

 

 

如果映射到進程C和進程B的(de)內存相應範(fan)圍內沒有數據,進程C 中的(de)內存錯誤(wu)(這個內存錯誤(wu)可能是用戶(hu)空間產(chan)生也可能是由(you)于用戶(hu)空間訪問內核si)佔洌 熱繽 opy_from_user()函數)將會導致ecryptfs讀取 /proc/$B/environ ,進而導致進程B中的(de)內存錯誤(wu),接下來導致ecryptfs讀取 /proc/$A/environ ,最後導致進程A中的(de)進程錯誤(wu)。如此sou) 吠矗 鈧找緋chu)內核棧,使內核崩(beng)潰。內核棧如下︰

 

[...]

[<ffffffff811bfb5b>]handle_mm_fault+0xf8b/0x1820

[<ffffffff811bac05>]__get_user_pages+0x135/0x620

[<ffffffff811bb4f2>]get_user_pages+0x52/0x60

[<ffffffff811bba06>]__access_remote_vm+0xe6/0x2d0

[<ffffffff811e084c>]? alloc_pages_current+0x8c/0x110

[<ffffffff811c1ebf>]access_remote_vm+0x1f/0x30

[<ffffffff8127a892>]environ_read+0x122/0x1a0

[<ffffffff8133ca80>]? security_file_permission+0xa0/0xc0

[<ffffffff8120c1a8>]__vfs_read+0x18/0x40

[<ffffffff8120c776>]vfs_read+0x86/0x130

[<ffffffff812126b0>]kernel_read+0x50/0x80

[<ffffffff81304d53>]ecryptfs_read_lower+0x23/0x30

[<ffffffff81305df2>]ecryptfs_decrypt_page+0x82/0x130

[<ffffffff813040fd>]ecryptfs_readpage+0xcd/0x110

[<ffffffff8118f99b>]filemap_fault+0x23b/0x3f0

[<ffffffff811bc120>]__do_fault+0x50/0xe0

[<ffffffff811bfb5b>]handle_mm_fault+0xf8b/0x1820

[<ffffffff811bac05>]__get_user_pages+0x135/0x620

[<ffffffff811bb4f2>]get_user_pages+0x52/0x60

[<ffffffff811bba06>]__access_remote_vm+0xe6/0x2d0

[<ffffffff811e084c>]? alloc_pages_current+0x8c/0x110

[<ffffffff811c1ebf>]access_remote_vm+0x1f/0x30

[<ffffffff8127a892>]environ_read+0x122/0x1a0

[...]

 

關于這個漏洞的(de)可利用性︰利用該漏洞,需要(yao)能夠(gou)掛載/proc/$pid為ecryptfs文件系統。安(an)裝完ecryptfs-utils包之(zhi)後(如果安(an)裝Ubuntu時(shi)選擇了home目錄加密, Ubuntu 會自動安(an)裝),使用 /sbin/mount.ecryptfs_私有的(de)setuid輔助(zhu)函數就可以做(zuo)到這一點。

大福彩票官网

接下來的(de)描述是平jiao)ㄏ喙氐de),這里指amd64。

以前要(yao)利用這一類(lei)漏洞還是相當簡單的(de),可以直接覆蓋棧底(di)的(de)thread_info結構體,用合(he)適的(de)數值重(zhong)寫restart_block或者 addr_limit,然(ran)後根據所用方式,選擇執(zhi)行用戶(hu)空間映射的(de)代碼,還是用copy_from_user() 和 copy_to_user() 直接讀寫內核數據。

但是,restart_block已經從thread_info結構體中移除,並且由(you)于棧溢出(chu)觸發時(shi)棧中有 kernel_read() 的(de)棧幀(zheng),所以addr_limit已經是KERNEL_DS,而且函數退出(chu)時(shi)將會重(zhong)置成 USER_DS 。另外, Ubuntu 16.04以後的(de)內核都打開了CONFIG_SCHED_STACHK_END_CHECK 內核配置選項。打開這個選項以後,每(mei)次調度到這個線程時(shi), thread_info 結構體上方的(de)金絲(si)雀值都會被(bei)檢查;如果金絲(si)雀值不正確的(de)話,內核遞歸就會出(chu)錯然(ran)後崩(beng)潰。

由(you)于thread_info結構體中很難照到有價值的(de)攻(gong)擊目標(biao)(同(tong)時(shi)移除thread_info中的(de)數據並非有效的(de)緩(huan)hang)jie)措施),我就選擇了其它方式︰溢出(chu)棧到棧之(zhi)前的(de)空間,然(ran)後利用棧huan)推淥詿嬋佔渲zhi)間會重(zhong)合(he)這一點。這種(zhong)方式的(de)問題就是一定要(yao)保(bao)證金絲(si)雀值和 thread_info結構中的(de)其它成員(yuan)不被(bei)覆蓋。棧溢出(chu)的(de)內存布局如下所示(綠色表示可以覆蓋,紅色表示jing)荒芨哺牽 粕 硎靖哺嗆罌贍芑嵊形侍猓 /p>

 

 

 

 

 

 

 

 

幸運(yun)的(de)是,有些棧幀(zheng)中存在空洞(如果遞歸的(de)最底(di)部采(cai)用cmdline而不是environ),遞歸的(de)過程中就會有一個5個QWORD空洞沒有被(bei)訪問到。這些空洞足夠(gou)用來存放從SRACK_END_MAIC到flags的(de)所有數據。這一點可以通過一個安(an)全遞歸和一個內核調試yue)?槔詞(ci)迪鄭 飧瞿諍說魘閱(yue)?榻 恢械de)所有空洞標(biao)綠便于觀察︰

 

 

 

 

 

 

 

 

接下來的(de)問題是空洞只會出(chu)現在特定的(de)位置,而漏洞利用就需要(yao)空洞在準確的(de)位置出(chu)現zheng)O旅嬗幸恍┘記煽梢雜美炊雲胝豢佔洌/p>

 

1.在每(mei)個遞歸層上都可以選擇“environ”文件或者“cmdline”文件,它們的(de)棧幀(zheng)大小(xiao)和空洞模式都不一樣。

2.任(ren)何調用copy_from_user()都會導致內存錯誤(wu)。甚至可以將寫入系統調用和VFS寫入句柄(bing)結合(he)起來,所以每(mei)一個寫入系統調用和 VFS寫入句柄(bing)都會影響深(shen)度(合(he)並深(shen)度可以計(ji)算(suan)出(chu)來,而不用測試每(mei)個變(bian)量)。

在測試了各種(zhong)組合(he)之(zhi)後,我找到一組environ文件和cmdline文件, 還有write ()系統調用和進程的(de)VFS寫句柄(bing)的(de)組合(he)。

隨後,就可以遞歸到之(zhi)前分配的(de)空間,而不會覆蓋任(ren)何危險數據了。然(ran)後暫停內核線程的(de)執(zhi)行,此時(shi)棧指針(zhen)指向之(zhi)前分配的(de)內存空間,這些內存空間應該用新的(de)棧來覆蓋,然(ran)後繼續(xu)內核線程的(de)執(zhi)行xiao)/p>

為了暫停遞歸中內核線程的(de)執(zhi)行,在建立起映射鏈(lian)後,映射鏈(lian)最後的(de)annonymous映射可以用FUSE映射取代( userfaultfd 函數並不適用,它不能捕捉遠程的(de)內存訪問)。

對于先前分配的(de)內存,我的(de)exp使用管(guan)道(Pipes)。當寫入數據到新分配的(de)空管(guan)道時(shi),伙伴內存分配器(qi)會分配一個內存頁(ye),來存放這些數據。我的(de)exp通過管(guan)道內存頁(ye)分配來填充大量內存,所以yun)褂lone()創建新進程時(shi)就會觸發內存錯誤(wu)。這里使用clone() 而非fork(),因為調用clone()時(shi)只要(yao)控制好參(can)數,系統就會復制較少的(de)信息,可以減少內存分配的(de)干(gan)擾(rao)。 Clone( ) 函數調用過程中,所有的(de)管(guan)道內存頁(ye)都被(bei)填充滿(man),除了第一次保(bao)存的(de) RIP值——遞歸進程暫停在FUSE中時(shi),它保(bao)存在期望的(de) RSP 值之(zhi)後。寫入較少的(de)數據就能致使第二個管(guan)道寫入目標(biao)棧數據,這些數據在 RIP控制實現zhong)zhi)前就被(bei)使用,可能會導致內核崩(beng)潰。隨後,遞歸進程在FUSE 中暫停時(shi),第二次向所有管(guan)道寫入數據,會覆蓋保(bao)存的(de) RIP值和其後的(de)數據,攻(gong)擊者也就能夠(gou)完全控制全新的(de)棧了。

 

 

 

 

此時(shi),最後一道防線就是KASLR了。Ubuntu支持KASLR ,只不過KASLR需要(yao)手動開啟。這個b最近該BUG已經修復了,現在distros內核應該是默認就開啟KASLR的(de)。雖說這項安(an)全特性幫(bang)不上太(tai)大的(de)忙,但畢竟KASLR不需要(yao)佔用太(tai)多資源,開啟這項特性就顯得相當beng)硭比ran)了。由(you)于大多數的(de)設備並不支持向內核命令(ling)行傳輸特殊(shu)參(can)數,所以這里假設KASLR雖然(ran)編(bian)譯進了內核,但仍處(chu)于未激活狀態(tai),攻(gong)擊者也知道內核代碼和靜(jing)態(tai)數據的(de)地址。

然(ran)後就可以用ROP在內核里做(zuo)各種(zhong)事(shi)情了,漏洞利用具體有兩個方向可以繼續(xu)。可以yun)褂OP進行 commit_creds 類(lei)似操作。不過我用了另一個方法(fa)。在棧溢出(chu)過程中,原來addr_limit的(de)KERNEL_DS 值保(bao)存了起來。棧一次次返回,最終將會把 addr_limit 重(zhong)置為USER_DS。但如果我們直接返回到用戶(hu)空間, addr_limit 將保(bao)持 KERNEL_DS 。所以我這樣構造新棧,或多或少復制了棧頂的(de)數據︰

 

unsigned longnew_stack[] = {

 0xffffffff818252f2,/* return pointer of syscall handler */

 /* 16 uselessregisters */

 0x1515151515151515,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 (unsignedlong) post_corruption_user_code, /* user RIP */

 0x33, /* userCS */

 0x246, /*EFLAGS: most importantly, turn interrupts on */

 /* user RSP*/

 (unsignedlong) (post_corruption_user_stack + sizeof(post_corruption_user_stack)),

 0x2b /* userSS */

};

 

殺掉FUSE服務進程後,遞歸進程繼續(xu)運(yun)行到post_corruption_user_code函數上。這個函數可以yun)褂霉guan)道向任(ren)意(yi)內核地址寫ci)藎 蛭copy_to_user()中的(de)地址檢查已經失(shi)效。

 

voidkernel_write(unsigned long addr, char *buf, size_t len) {

  int pipefds[2];

  if (pipe(pipefds))

    err(1, 'pipe');

  if (write(pipefds[1], buf, len) != len)

    errx(1, 'pipe write');

  close(pipefds[1]);

  if (read(pipefds[0], (char*)addr, len) !=len)

    errx(1, 'pipe read tokernelspace');

  close(pipefds[0]);

}

 

現在你就可以在用戶(hu)態(tai)舒服地執(zhi)行任(ren)意(yi)讀寫操作了。如果你想要(yao)root shell,可以覆蓋coredump函數,它存儲在一個靜(jing)態(tai)變(bian)量里,然(ran)後觸發一個 SIGSEGV,就可以以root權限執(zhi)行coredump函數︰

 

 char*core_handler = '/tmp/crash_to_root';

 kernel_write(0xffffffff81e87a60,core_handler, strlen(core_handler)+1);

 

大福彩票官网

有兩個獨立的(de)補(bu)丁可用于修復該BUG︰其中,2f36db710093 禁止通過ecryptfs打開沒有mmap函數的(de)文件,

Tag標(biao)簽(qian)︰遞歸  內核  漏洞  
  • 大福彩票官网

About IT165 - 廣告(gao)服務 - 隱私聲明 - 版權申明 - 免責(ze)條(tiao)款 - 網站地圖 - 網友投(tou)稿(gao) - 聯系方式
本(ben)站內容來自于互聯網,僅(jin)供用于網絡技術學習,學習中請遵(zun)循相關法(fa)律法(fa)規(gui)
大福彩票官网 | 下一页