Linux下動態庫是通過mmap
建立起內存和文件的映射關係。其定義如下void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
,在第一個參數start
爲NULL
的時候系統會隨機分配一個地址,我們可以通過示例來看mmap
映射地址的流程。
分析一下程序加載libc.so
的流程
open(“/ lib / libc.so.6 ”,O_RDONLY)= 3 讀取(3,“\ 177ELF \ 1 \ 1 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 3 \ 0 \ 3 \ 0 \ 1 \ 0 \ 0 \ 0 n \ 1 \ 0004 \ 0 \ 0 \ 0“ ...,512)= 512 fstat64(3,{st_mode = S_IFREG | 0755,st_size = 1409436,...})= 0 mmap2(NULL,1415560,PROT_READ | PROT_EXEC,MAP_PRIVATE | MAP_DENYWRITE,3,0)= 0xb75b1000 mmap2(0xb7705000,12288,PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_FIXED | MAP_DENYWRITE,3,0x154)= 0xb7705000 mmap2(0xb7708000,10632,PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS,-1,0)= 0xb7708000 接近(3)
在通常情況下通過mmap
映射的地址會被內核進行隨機化處理,所以每次程序運行加載的動態庫基址都不相同。
〜$ ldd mmap linux-gate.so.1 =>(0xb77d9000) libc.so.6 => /lib/libc.so.6(0xb7654000) /lib/ld-linux.so.2(0xb77bd000) 〜$ ldd mmap linux-gate.so.1 =>(0xb7738000) libc.so.6 => /lib/libc.so.6(0xb75b3000) /lib/ld-linux.so.2(0xb771c000
0x01 CVE-2016-3672
Linux kernel 4.5.2之前版本,arch/x86/mm/mmap.c內函數arch_pick_mmap_layout未正確隨機化遺留基址。本地用戶禁用棧資源消耗限制後,可破壞ADDR_NO_RANDOMIZE標記的限制,繞過setuid或setgid程序的ASLR保護機制。
這個漏洞在32位操作系統或者在64位操作系統運行32位程序時,將棧空間設置爲不限制,會導致mmap
的ASLR失效,導致動態庫加載的地址固定。
驗證方法:
- 設置棧空間爲不限制大小
ulimit -s unlimited
- 使用ldd看動態庫加載的地址是否發生變化
〜$ ldd mmap linux-gate.so.1 =>(0xb77d9000) libc.so.6 => /lib/libc.so.6(0xb7654000) /lib/ld-linux.so.2(0xb77bd000) 〜$ ldd mmap linux-gate.so.1 =>(0xb7738000) libc.so.6 => /lib/libc.so.6(0xb75b3000) /lib/ld-linux.so.2(0xb771c000) 〜$ ulimit -s無限制 〜$ ldd mmap linux-gate.so.1 =>(0x4001c000) libc.so.6 => /lib/libc.so.6(0x4002e000) /lib/ld-linux.so.2(0x40000000) 〜$ ldd mmap linux-gate.so.1 =>(0x4001c000) libc.so.6 => /lib/libc.so.6(0x4002e000) /lib/ld-linux.so.2(0x40000000)
可見,設置了棧空間不限制大小後,動態庫的基址就固定了。
0x02 漏洞分析
漏洞所在函數爲arch_pick_mmap_layout
/ * *這個功能,在創建新功能的過程中非常早 *處理VM映像,設置要使用的VM佈局功能: * / void arch_pick_mmap_layout (struct mm_struct * mm) { mm-> mmap_legacy_base = mmap_legacy_base(); mm-> mmap_base = mmap_base(); if(mmap_is_legacy()){ mm-> mmap_base = mm-> mmap_legacy_base; mm-> get_unmapped_area = arch_get_unmapped_area; } else { mm-> get_unmapped_area = arch_get_unmapped_area_topdown; } }
如果讓ASLR失效則需要讓mm->mmap_base
爲固定值。看看mmap_legacy_base
/ * * X86_32上的自下而上(傳統)佈局不支持隨機化,X86_64 *但是在模擬X86_32時沒有 * / static unsigned long mmap_legacy_base (void) { if(mmap_is_ia32()) 返回 TASK_UNMAPPED_BASE; 其他 返回 TASK_UNMAPPED_BASE + mmap_rnd(); }
可以看到mmap_is_ia32()
爲真時,返回的地址爲固定值。註釋更表明了影響32位機器和在64位機器上運行的32位程序。此時,只需要mmap_is_legacy()
爲真。
/ * * mmap區域的頂部(就在進程堆棧下方)。 * *留下至少~128 MB的空洞,可能有堆棧隨機化。 * / #限定 MIN_GAP(128 * 1024 * 1024UL + stack_maxrandom_size()) #限定 MAX_GAP(TASK_SIZE / 6 * 5) static int mmap_is_legacy (void) { if(current-> personality&ADDR_COMPAT_LAYOUT) 返回 1 ; if(rlimit(RLIMIT_STACK)== RLIM_INFINITY) 返回 1 ; return sysctl_legacy_va_layout; }
注意到rlimit(RLIMIT_STACK) == RLIM_INFINITY
則返回真,這就是ulimit -s unlimited
的原因。
0x03 修復方案分析
diff --git a / arch / x86 / mm / mmap.cb / arch / x86 / mm / mmap.c index 96bd1e2..389939f 100644 --- a / arch / x86 / mm / mmap.c +++ b / arch / x86 / mm / mmap.c @@ -94,18 +94,6 @@ static unsigned long mmap_base(unsigned long rnd) } / * - * X86_32上的自下而上(傳統)佈局不支持隨機化,X86_64 - *確實如此,但在模擬X86_32時卻沒有 - * / -static unsigned long mmap_legacy_base(unsigned long rnd) - { - if(mmap_is_ia32()) - 返回TASK_UNMAPPED_BASE; - 否則 - 返回TASK_UNMAPPED_BASE + rnd; - } - - / * *這個功能,在創建新功能的過程中非常早 *處理VM映像,設置要使用的VM佈局功能: * / @@ -116,7 +104,7 @@ void arch_pick_mmap_layout(struct mm_struct * mm) if(current-> flags&PF_RANDOMIZE) random_factor = arch_mmap_rnd(); - mm-> mmap_legacy_base = mmap_legacy_base(random_factor); + mm-> mmap_legacy_base = TASK_UNMAPPED_BASE + random_factor; if(mmap_is_legacy()){ mm-> mmap_base = mm-> mmap_legacy_base;
很簡單,不管是以lagacy模式運行還是真正的32位程序,mmap
的基址mmap_base
均加入隨即因子進行隨機化
0x04 題外
在64位機器上發現也存在ASLR失效的問題,不過vDSO
還是有隨機化保護的。留個坑有時間在看看。
0x05 Refer
http://rk700.github.io/2016/11/22/mmap-aslr/ http://lists.alioth.debian.org/pipermail/kernel-svn-changes/2016-April/023114.html http://hmarco.org/bugs/CVE-2016-3672-Unlimiting-the-stack-not-longer-disables-ASLR.html