IT技術互動交流平臺

Linux內核中的遞歸漏洞利用

發布日期:2016-06-30 22:25:39

背景知識

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

Linux內核棧(可以用來處理系統調用)和用戶態的棧很不一樣。內核棧相對來說更短:32位x86架構平臺為4096byte , 64位系統則有16384byte(內核棧大小由THREAD_SIZE_ORDER 和 THREAD_SIZE 確定)。它們是由內核的伙伴內存分配器分配,伙伴內存分配器是內核常用來分配頁大。ㄒ约绊摯笮”稊担﹥却娴姆峙淦,它不創建內存保護頁。也就是說,如果內核棧溢出的話,它將直接覆蓋正常的數據。正因如此,內核代碼必須(通常也是)在棧上分配大內存的時候非常小心,并且必須阻止過多的遞歸。

Linux上的大多數文件系統既不用底層設備(偽文件系統,比如sysfs, procfs, tmpfs等),也不用塊設備(一般是硬盤上的一塊)作為備用存儲設備。然而, ecryptfs 和overlayfs是例外。這兩者是堆疊的文件系統,這種文件系統會使用其他文件系統上的文件夾作為備用存儲設備(overlayfs則使用多個不同文件系統上的多個文件作為備用存儲設備)。被用作備用存儲設備的文件系統稱為底層文件系統,其上的文件稱為底層文件。這種層疊文件系統的特點是它或多或少的會訪問底層文件系統,并對訪問的數據做一些修改。 Overlayfs融合多個文件系統,ecryptfs則進行了相應的加密。

層疊文件系統實際上存在潛在風險,因為其訪問虛擬文件系統的函數常會訪問到底層文件系統的函數,相較直接訪問底層文件系統的句柄,這會增大?臻g?紤]這樣一個場景:如果用層疊文件系統作為另外一個層疊系統的備用存儲設備,由于每一層文件系統的堆疊都增大了?臻g,內核棧就會在某些情況下溢出。但是,設置FILESYSTEM_MAX_STACK_DEPTH 限制文件系統的層數,只允許最多兩層層疊文件系統放在非層疊文件系統上,就可以避免這個問題。

在Procfs偽文件系統上,系統中運行的每一個進程都有一個文件夾,每個文件夾包含一些描述該進程的文件。值得注意的是每個進程的“mem”,“ environ”和“cmdline”文件,因為訪問這些文件會同步訪問目標進程的虛擬內存。這些文件顯示了不同的虛擬內存地址范圍:

 

1.“mem”文件顯示了整個虛擬內存地址范圍(需要PTRACE_MODE_ATTACH 權限)

2.“environ”文件顯示了mm->env_start 到mm->env_end的內存范圍(需要PTRACE_MODE_READ權限)

3.“cmdline”文件顯示了mm->arg_start 到mm->arg_end的地址范圍(如果mm->arg_end的前一個字符是null 的話)

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

 

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

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

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

 

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文件系統,比較有趣的一個細節在于它支持mmap()。用戶看到的內存映射必須是解密的,而底層文件系統的內存映射是加密的,因而ecryptfs 文件系統不能將mmap()函數直接映射到底層文件系統的mmap()函數上。Ecrypt 文件系統在內存映射時使用了自己的頁緩存。

ecryptfs文件系統處理頁錯誤的時候,必須以某種方式讀取底層文件系統上加密的頁。這可以通過讀取底層文件文件系統的頁緩存(使用底層文件系統的mmap函數)來實現,但是