APP漏洞掃描器之未使用地址空間隨機化
前言
本文主要介紹該項技術的原理和掃描器的檢測方法。 由于PIE的實 現細節較復雜,本文只是介紹了大致的原理。
PIE是什么
PIE(position-independent executable)是一種生成地址無關可執行程序的技術。 如果編譯器在生成可執行程序的過程中使用了PIE,那么當可執行程序被加載到內存中時其加載地址存在不可預知性。
PIE還有個孿生兄弟PIC(position-independent code)。其作用和PIE相同,都是使被編譯后的程序能夠隨機的加載到某個內存地址。區別在于PIC是在生成動態鏈接庫時使用(Linux中的so),PIE是在生成可執行文件時使用。
PIE的作用
安全性
PIE可以提高緩沖區溢出攻擊的門檻。它屬于ASLR(Address space layout randomization)的一部分。ASLR要求執行程序被加載到內存時,它其中的任意部分都是隨機的。 包括 Stack, Heap, Libs and mmap, Executable, Linker, VDSO 。通過PIE我們能夠實現Executable 內存隨機化。
節約內存使用空間
除了安全性,地址無關代碼還有一個重要的作用是提高內存使用效率。
一個共享庫可以同時被多個進程裝載,如果不是地址無關代碼(代碼段中存在絕對地址引用),每個進程必須結合其自生的內存地址調用動態鏈接庫。導致不得不將共享庫整體拷貝到進程中。如果系統中有100個進程調用這個庫,就會有100份該庫的拷貝在內存中,這會照成極大的空間浪費。
相反如果被加載的共享庫是地址無關代碼,100個進程調用該庫,則該庫只需要在內存中加載一次。這是因為PIE將共享庫中代碼段須要變換的內容分離到數據段。使得代碼段加載到內存時能做到地址無關。多個進程調用共享庫時只需要在自己的進程中加載共享庫的數據段,而代碼段則可以共享。
PIE工作原理簡介
我們先從實際的例子出發,觀察PIE和NO-PIE在可執行程序表現形式上的區別。管中窺豹探索地址無關代碼的實現原理。
例子一
定義如下C代碼:
程序中定義了一個全局變量global并打印其地址。我們先用普通的方式編譯程序。
運行程序可以觀察到global加載到內存的地址每次都一樣。
接著用PIE方式編譯 sample1.c
運行程序觀察global的輸出結果:
每次運行地址都會發生變換,說明PIE使執行程序每次加載到內存的地址都是隨機的。
例子二
在代碼中聲明一個外部變量global。但這個變量的定義并未包含進編譯文件中。
首先使用普通方式編譯 extern_var.c。在編譯選項中故意不包含有global定義的源文件。
發現不能編譯通過, gcc提示:
編譯器在鏈接階段有一步重要的動作叫 符號解析與重定位 。鏈接器會將所有中間文件的數據,代碼,符號分別合并到一起,并計算出鏈接后的虛擬基地址。比如 “.text”段從 0x1000開始,”.data”段從0x2000開始。接著鏈接器會根據基址計算各個符號(global)的相對虛擬地址。
當編譯器發現在符號表中找不到global的地址時就會報出 undefined reference to `global` . 說明在靜態鏈接的過程中編譯器必須在編譯鏈接階段完成對所有符號的鏈接。
如果使用PIE方式將extern_var.c編譯成一個share library會出現什么情況呢?
程序能夠順利編譯通過生成extern_var.so。但運行時會報錯,因為裝載時找不到global符號目標地址。這說明-fPIC選項生成了地址無關代碼。將靜態鏈接時沒有找到的global符號的鏈接工作推遲到裝載階段。
那么在編譯鏈接階段,鏈接器是如何將這個缺失的目標地址在代碼段中進行地址引用的呢?
鏈接器巧妙的用一張中間表GOT(Global Offset Table)來解決被引用符號缺失目標地址的問題。如果在鏈接階段(jing tai)發現一個不能確定目標地址的符號。鏈接器會將該符號加到GOT表中,并將所有引用該符號的地方用該符號在GOT表中的地址替換。到裝載階段動態鏈接器會將GOT表中每個符號對應的實際目標地址填上。
當程序執行到符號對應的代碼時,程序會先查GOT表中對應符號的位置,然后根據位置找到符號的實際的目標地址。
地址無關代碼的生成方式
所謂地址無關代碼要求程序被加載到內存中的任意地址都能夠正常執行。所以程序中對變量或函數的引用必須是相對的,不能包含絕對地址。
比如如下偽匯編代碼:
PIE方式: 代碼可以運行在地址100或1000的地方
Non-PIE: 代碼只能運行在地址100的地方
因為可執行程序的代碼段只有讀和執行屬性沒有寫屬性,而數據段具有讀寫屬性。要實現地址無關代碼,就要將代碼段中須要改變的絕對值分離到數據段中。在程序加載時可以保持代碼段不變,通過改變數據段中的內容,實現地址無關代碼。
PIE和Non-PIE程序在內存中映射方式
在Non-PIE時程序每次加載到內存中的位置都是一樣的。
執行程序會在固定的地址開始加載。系統的動態鏈接器庫ld.so會首先加載,接著ld.so會通過.dynamic段中類型為DT_NEED的字段查找其他需要加載的共享庫。并依次將它們加載到內存中。 注意:因為是Non-PIE模式,這些動態鏈接庫每次加載的順序和位置都一樣。
而對于通過PIE方式生成的執行程序,因為沒有絕對地址引用所以每次加載的地址也不盡相同。
不僅動態鏈接庫的加載地址不固定,就連執行程序每次加載的地址也不一樣。這就要求 ld.so 首先被加載后它不僅要負責重定位其他的共享庫,同時還要對可執行文件重定位。
PIE與編譯器選項
GCC編譯器用于生成地址無關代碼的參數主要有-fPIC, -fPIE, -pie。
其中-fPIC, -fPIE屬于編譯時選項,分別用于生成共享庫和可執行文件。它們能夠使編譯階段生成的中間代碼具有地址無關代碼的特性。但這并不代表最后生成的可執行文件是PIE的。還需要在鏈接時通過-pie選項告訴鏈接器生成地址無關代碼的可執行程序。
一個標準的PIE程序編譯設置如下:
在gcc中使用編譯選項與是否生成PIE可執行文件對應關系如下:
Type為DYN的程序支持PIE,EXEC類型不支持。DYN, EXEC與PIE對應關系詳見后文。
ASLR在Android中的應用
PIE屬于ASLR的一部分,如上節提到ASLR包括對Stack, Heap, Libs and mmap, Executable, Linker, VDSO的隨機化。 而支持PIE只表示對Executable實現了ASLR。隨著Android的發展對ASLR的支持也逐漸增強。
ASLR in Android 2.x
Android對ASLR的支持是從Android 2.x開始的。2.x只支持對Stack的隨機化。
ASLR in Android 4.0
而在4.0,即其所謂的支持ASLR的版本上,其實ASLR也僅僅增加了對libc等一些shared libraries進行了隨機化,而對于heap, executable和linker還是static的。
對于heap的隨機化來說,可以通過
來開啟。
而對于executable的隨機化,由于大部分的binary沒有加GCC的-pie -fPIE選項,所以編譯出來的是EXEC,而不是DYN這種shared object file,因此不是PIE(Position Independent Executable),所以沒有辦法隨機化;
同樣的linker也沒有做到ASLR。
ASLR in Android 4.1
終于,在4.1 Jelly Bean中,Android支持了所有內存的ASLR。在Android 4.1中,基本上所有binary都被編譯和連接成了PIE模式(可以通過readelf查看其Type)。所以,相比于4.0,4.1對Heap,executable和linker都提供了ASLR的支持。
ASLR in Android 5.0
5.0中Android拋棄了對non-PIE的支持,所有的進程均是ASLR的。如果程序沒有開啟PIE,在運行時會報錯并強制退出。
PIE程序運行在Android各版本
支持PIE的可執行程序只能運行在4.1+的版本上。 在4.1版本之前運行會出現crash。而Non-PIE的程序,在5.0之前的版本能正常運行,但在5.0上會crash。
如何檢測是否開啟PIE
未開啟PIE的執行程序用readelf查看其文件類型應顯示EXEC(可執行文件),開啟PIE的可執行程序的文件類型為DYN(共享目標文件)。另外代碼段的虛擬地址總是從0開始。
為什么檢測DYN就可以判斷是否支持PIE?
DYN指的是這個文件的類型,即共享目標文件。那么所有的共享目標文件一定是開啟了PIE的嗎?我們可以從源碼中尋找答案。查看glibc/glibc-2.16.0/elf/dl-load.c中的代碼。
從源碼可知,如果加載類型不為ET_DYN時調用mmap加載文件時會傳入MAP_FIXED標志。將程序映射到固定地址。
阿里聚安全對開發者建議
1、針對Android 2.x-4.1之前的系統在編譯時不要使用生成PIE的選項。
2、在Android 4.1以后的版本必須使用PIE生成Native程序,提高攻擊者中的攻擊成本。
3、在版本上線前使用阿里聚安全漏洞掃描系統進行安全掃描,將安全隱患阻擋在發布之前。
Reference
https://en.wikipedia.org/wiki/Position-independent_code
http://www.openbsd.org/papers/nycbsdcon08-pie/
https://source.android.com/security/enhancements/enhancements50.html
http://ytliu.info/blog/2012/12/09/aslr-in-android/
https://codywu2010.wordpress.com/2014/11/29/about-elf-pie-pic-and-else/
http://www.cnblogs.com/huxiao-tee/p/4660352.html
http://stackoverflow.com/questions/5311515/gcc-fpic-option
來自:http://jaq.alibaba.com/community/art/show?articleid=640