PaX介紹——針對linux kernel的一個加固版本的補丁,是這個星球上有史以來最極端和最優秀的防御系統級別0day的方案


正文摘要

  • PaX是針對linux kernel的一個加固版本的補丁,它讓linux內核的內存頁受限於最小權限原則,是這個星球上有史以來最極端和最優秀的防御系統級別0day的方案,第1版的設計和實現誕生於2000年,那可是一個沒有ASLR/RELRO/NX/CANARY/FORITY/PIE都沒有的年代

  • 今天意義上的現代mitigation技術不管是linux/windows/macosx都多少抄襲和模仿了PaX的設計和實現

  • 當年Linux內核不收PaX進入upstream是因為很多人覺得PaX不是那么的好維護,之后linux內核推出了LSM( Linux Security Module),LSM利用了一堆CAPABILITY的 機制提供了一些限制用戶態程序訪問控制的接口,SELinux和Apparmor就是基於LSM開發的,注意LSM並不是一個傳統意義上的linux kernel module,至少在2個地方不同於普通module: 1) 必須在bootloader啟動內核時啟動,不能在內核加載完后啟動。 2) 不能同時啟動2個LSM的實現。

  • 但PaX Team是一群old school security hackers,他們認為LSM打破了 “security as a whole”的哲學,所以寧願單獨維護一個patch

  • 談到PaX時都會寫Grsecurity/PaX,這是怎么回事呢?PaX從一開始就主要關注如何防御和檢測memory corruption,后來Grsecurity社區發現PaX和他們所關注的非常類似,所以就合並了,在很長的一段時間里PaX主要關注memory corruption,而Grsecurity則實現其他的功能包括RBAC,但到最近2個社區的工作開始模糊了:包括USERCOPY, STACKLEAK, RANDSTRUCT, etc..都是整個Grsecurity/PaX共同實現的特性。

PaX的誕生

1999年7月的plex86社區(old school虛擬化社區之一)打算驗證一個概念,當時Pentium(包括P6family)處理器新增加了一個功能,就是CPU把TLB區分為DTLB(數據TLB)和ITLB(指令TLB),TLB主要是PTE( page table entries)的緩存,因此存放着user/kernel spaces的訪問權限信息,在正常的情況下,ITLB和DTLB entries從相同的PTE里讀出相同的狀態,但如果狀態有所改變的話也就意味着可以把數據讀寫和代碼執行分開,如果這個POC能成功也就意味着可以對抗緩沖區溢出的最佳方
案,這個成為了今天的NX=>要么可讀寫要么可執行。

PaX在出現page fault的時候多增加了一些動作包括模擬page table entry里的可訪問U標志位和在模擬PTE中檢查訪問權限。

PaX的第一版的副作用也不小,

1,用戶態可執行的棧是不可能的
2,性能損耗在5%–8%

2003年PaX談”未來”

從defensive的平面來看,當時GNU/Linux平台主要依賴PaX的patch來進行加固(包括ASLR),ASLR和NX進入linux內核mainstream是后來的事情,OpenBSD和WindowsXP SP2和OSX 10.5也加入了NX,但都是抄襲PaX的設計(或許也包括實現),

從offensive的平面來看,2003年的背景是stack-based overflow和string format vuln已經泛濫,但ROP還沒有大規模的流行,但old school社區對於ROP的研究已經有相當的研究

2003年,PaX team認為會導致漏洞利用的bug給予了攻擊者(區分攻擊者和黑客是不同的term)在3個不同層面上訪問被攻擊的進程:

(1) 執行任意代碼
(2) 執行現有代碼但打破了原有的執行順序
(3) 原有的執行順序執行現有代碼,但加載任意數據

NOEXEC( Non-executable pages)和MPROTECT(mmap/mprotect)能防御(1),但有一種情況是例外:如果攻擊者能創建和寫入一個文件然后mmap()到被攻擊的進程空間里,這樣可以執行任意的代碼。

ASLR在一定程度上降低了(1),(2),(3)的風險,但如果內核有信息泄露的bug例外。PaX team在當時就認為把內核當成可信計算( Trusted Computing)的基礎是一件可笑的事情,因為內核跟用戶空間一樣容易遭受各種攻擊。所以他們認為”未來”需要做一些事情(注:這些事情今天都已經搞定):

(a) 嘗試處理(1)不能處理的那個例外情況
(b) 實現所有可能在內核態自己的防御機制
(c) 為(2)實現確定性( deterministic)防護,可能也為(3)實現類似的機制
(d) 為(2)實現概率行( probalilistic)防護以實現阻止信息泄露

之后這篇文檔里詳細的羅列了針對(a)(b)(c)(d)需要去實現的加固方案。在下面
其實已經能看出一些后來出現的mitigation技術:Stack Canary, RELRO,

2003年PaX里vma mirroring的設計

在2003年的晚些時候PaX實現了虛擬內存空間的鏡像( vma mirroring)[8],vma mirroring的目的是為了在一組物理頁上做特殊文件隱射時有2個不同的線性地址,這2個地址即使在swap-out/swap-in或者COW后依然是不會改變的。這樣做的目的為了滿足幾種場景:

1,把可執行的區域在使用SEGMEXEC隱射進入代碼段。在32-bit的linux內核里的4GB地址空間是其中3GB給用戶空間,1GB給內核空間,而vma mirroring把用戶空間的3GB划分成了2個1.5GB分別是給代碼段和數據段,在可執行區域里包含的數據的部分(常量字符串,函數指針表等)都會mirroring到數據段里。

2,實現可執行區域的地址隨機化( RANDEXEC)。

3,這個引出了第3種情況,就是SEGMEXEC和RANDEXEC同時激活,個人覺得這個的效果應該和PIE+ASLR的效果類似,不同的不是整個elf binary的代碼段隨機化,而是在mirroring時對代碼段和數據段進行隨機化。

當時的PaX的做法大致是這樣的,vma mirror是根據已經內存映射mmap()后的地址,用戶態通過mmap()是無法直接去做vma mirror請求的,所有的mmap()請求多會經過include/linux/mm.h的do_map(),PaX擴展( SEGMEXEC)也是在這個地方處理,原始內核通過調用do_mmap_pgoff()來調用do_mmap(),PaX在這里為了確保SEGMEXEC能知道來自用戶態和內核態的原生文件映射請求所以略過do_mmap_pgoff()而直接調用do_mmap(),而vma mirror請求使用一些特殊參數傳遞給do_mmap_pgoff():

‘file’ 必須是NULL,因為mirror會引用相同文件的vma作為鏡像
‘addr’ 正常使用
‘len’ 必須是0
‘prot’ 正常使用
‘flags’ 正常使用,除了一種情況:指定MAP_MIRROR和只能指定private映射
‘pgoff’ 指定vma的線性起始地址作為鏡像

舉個例子

#cp /bin/cat /tmp/ #/tmp/cat /proc/self/maps
  • 1
  • 2
  • 激活PaX的2個功能: SEGMEXEC, MPROTECT
    [1] 08048000-0804a000 R-Xp 00000000 00:0b 1109 /tmp/cat [2] 0804a000-0804b000 RW-p 00002000 00:0b 1109 /tmp/cat [3] 0804b000-0804d000 RW-p 00000000 00:00 0 [4] 20000000-20015000 R-Xp 00000000 03:07 110818 /lib/ld-2.2.5.so [5] 20015000-20016000 RW-p 00014000 03:07 110818 /lib/ld-2.2.5.so [6] 2001e000-20143000 R-Xp 00000000 03:07 106687 /lib/libc-2.2.5.so [7] 20143000-20149000 RW-p 00125000 03:07 106687 /lib/libc-2.2.5.so [8] 20149000-2014d000 RW-p 00000000 00:00 0 [9] 5fffe000-60000000 RW-p fffff000 00:00 0 [10] 68048000-6804a000 R-Xp 00000000 00:0b 1109 /tmp/cat [11] 80000000-80015000 R-Xp 00000000 03:07 110818 /lib/ld-2.2.5.so [12] 8001e000-80143000 R-Xp 00000000 03:07 106687 /lib/libc-2.2.5.so 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

這個binary是一個動態連接的可執行程序,所以在執行時會映射其他的庫文件。

[1] 這個binary文件/tmp/cat的第1個PT-LOAD段映射為有讀和執行的權限,包含
了可執行的代碼和只讀的初始化后的數據。因為是可執行的所以被[10]鏡像。

[2] 第2個PT_LOAD段,映射為讀寫權限,包含了可寫的數據(所有初始化和沒有初
始化的)

[3] brk()管理的堆,在運行時會根據malloc()/free()來調整大小

[4][5] 動態連接器

[6][7] C庫 ,[4][6]被映射到了[11][12],因為他們是可執行的。

[8] 一個針對C庫的初始化數據的匿名映射

[9] 一個匿名映射包含了stack。我們能觀察到這個地址在用戶空間的數據部分的
結束地址,開啟SEGMEXEC后是TASK_SIZE/2。

[10][11][12] 分別映射可執行鏡像[1][4][6]。

  • 激活PaX的3個功能的情況: SEGMEXEC,RANDEXEC,MPROTECT
    [1] 08048000-0804a000 R-Xp 00000000 00:0b 1109 /tmp/cat [2] 0804a000-0804b000 RW-p 00002000 00:0b 1109 /tmp/cat 0804b000-0804d000 RW-p 00000000 00:00 0 [3] 20000000-20002000 ++-p 00000000 00:00 0 [4] 20002000-20003000 RW-p 00002000 00:0b 1109 /tmp/cat 20003000-20018000 R-Xp 00000000 03:07 110818 /lib/ld-2.2.5.so 20018000-20019000 RW-p 00014000 03:07 110818 /lib/ld-2.2.5.so 20021000-20146000 R-Xp 00000000 03:07 106687 /lib/libc-2.2.5.so 20146000-2014c000 RW-p 00125000 03:07 106687 /lib/libc-2.2.5.so 2014c000-20150000 RW-p 00000000 00:00 0 [5] 5fffe000-60000000 RW-p 00000000 00:00 0 [6] 80000000-80002000 R-Xp 00000000 00:0b 1109 /tmp/cat 80003000-80018000 R-Xp 00000000 03:07 110818 /lib/ld-2.2.5.so 80021000-80146000 R-Xp 00000000 03:07 106687 /lib/libc-2.2.5.so
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

RANDEXEC有一些改變,
[3]成了第1個可執行的PT_LOAD段的匿名映射,
[4]成為第2個PT_LOAD段的的mirror,[2]和[4]有相同的頁偏移值,文檔說[1]被[6]給
mirror后是超出了TASK/SIZE/2的范圍,但個人覺得這個地方是代碼段的區域所以
必然是在1.5G以上(如果數據段在0-1.5G的話),還有就是在RANDUSTACK開啟后由
於stack的第1部分不能關閉隨機化,所以多比第1個例子多占了1個page,這個怎
么得出的呢?靠我真不知道,可能是fffff000 xor ffffffff = fff來的?

  • 激活PaX的3個功能的情況: PAGEEXEC, RANDEXEC, MPROTECT
    [1] 08048000-0804a000 R--p 00000000 00:0b 1109 /tmp/cat [2] 0804a000-0804b000 RW-p 00002000 00:0b 1109 /tmp/cat 0804b000-0804d000 RW-p 00000000 00:00 0 [3] 40000000-40002000 R-Xp 00000000 00:0b 1109 /tmp/cat [4] 40002000-40003000 RW-p 00002000 00:0b 1109 /tmp/cat 40003000-40018000 R-Xp 00000000 03:07 110818 /lib/ld-2.2.5.so 40018000-40019000 RW-p 00014000 03:07 110818 /lib/ld-2.2.5.so 40021000-40146000 R-Xp 00000000 03:07 106687 /lib/libc-2.2.5.so 40146000-4014c000 RW-p 00125000 03:07 106687 /lib/libc-2.2.5.so 4014c000-40150000 RW-p 00000000 00:00 0 bfffe000-c0000000 RW-p fffff000 00:00 0 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

最后的這種情況是vma mirroring所產生最簡單的內存layout,只有binary本生被
鏡像了,[1]被[3],[2]被[4]鏡像了。注意[1]沒有R-X權限了,在PAGEEXEC下只
有R–。

雖然現在的PaX實現肯定不是這個設計的版本,但讀讀原始的paper會有一些意想
不到的收獲,也算技術進化考古的過程了;-)

配置使用

GRSecurity 為ACL系統提供了內核模塊的支持,另外,在用戶空間,還需要有gradm這個工具。
ACL是 Access Control List的簡寫,支持ACL的系統可以對系統及系統文件,系統資源進行細力度的訪問控制。
GRSecurity 還沒有集成到linux內核中,如想使用需要自己下載相關內核補丁。目前grsecurity的補丁穩定版支持2.6.27.10的內核,測試版支持到了最新的2.6.31.5版內核。

給內核打補丁的步驟

tar -jxf linux-<version>.tar.bz2 patch -p0 <grsecurity-<version>.patch cd linux-<version> make menuconfig
  • 1
  • 2
  • 3
  • 4

在Security options中就可以找到grsecurity的選項。

基本命令

啟動grsecurity gradm -E
停止grsecurity gradm -D
切換到管理員角色 gradm -a admin ,注意,-a 參數只能用於從一個普通角色切換到管理員角色

角色 subject object

要實現ACL(訪問控制),需要有一個策略文件,grsecurity啟動后,會根據該文件中的策略對系統進行訪問控制。
該文件位於/etc/grsec/目錄下,文件名為policy。

policy中有三個概念:
第一是角色role,如“role admin sA“,“role default ”,”role root uG“,表明有三個角色,分別是admin , default, root,至於角色與角色之間的關系,以及參數的含義,我們會在角色的定義這一節中講到。

第二是subject,每個subject中首先定義了一個可執行程序(注意,一個subject中只定義一個可執行程序,要對多個可執行程序進行定義的話,就需要多個subject),或者更准確一點,定義了一個運行在系統中的進程。然后后面跟着一系列的object(第三個概念),用來規定當前這個進程的權限。每個角色定義的后面都可以跟一個或多個subject,例如角色default后面,就定義了一個subject,“subject /”,而角色root的后面就定義了多個subject,“subject /”,“subject /usr/X11R6/bin/XFree86”,“subject /usr/bin/ssh” ,“subject /sbin/klogd”。

第三是object,每個subject中都會有若干的object,表示每個進程都有若干個操作對象,這些操作對象一般來說都是一些目錄,文件等等,用來規定當前這個subject中的進程對這些文件擁有哪些權限,例如:
subject /usr/bin/ssh /etc/ssh/ssh_config r
表示/usr/bin/ssh這個進程對/etc/ssh/ssh_config這個文件有讀權限(r)。更進一步講,這個subject位於角色root后面,所以這兩行策略的含義就是以root角色運行ssh時,ssh進程對/etc/ssh/ssh_config有讀權限。該策略只定義在了root角色中,對於其他角色不起作用。

注意:在每一個角色中,都必須有一個subject /,表示一個缺省的ACL,如果沒有這個缺省的ACL,grsecurity啟動時會報錯同時啟動失敗。當以一個角色登錄系統后,如果要執行的可執行程序沒有被某個subject定義,那么,該程序就會采用subject /中的缺省定義。例如root角色,沒有對cat命令進行定義,所以以root角色執行cat命令時,ACL系統就會參照subject /中的定義來控制cat進程對文件的訪問。

角色的定義

grsecurity的角色定義非常簡單,只需要在policy中聲明一下就可以了,語法為: role <role name> <parameter>,如例子中的role root uG
grsecurity中的角色分為用戶角色,組角色,缺省角色(default),還有個管理員角色。定義不同角色需要有不同的參數。
定義用戶角色,需要加參數“u”。定義組角色,需要加參數“g”,定義缺省角色可以什么參數都不加,定義管理員角色需要加”A”和“s”。注意,grsecurity的角色與用戶是一對一的。假設有tester用戶,屬於test組,那么tester用戶登錄后,會先在配置文件中匹配名為tester用戶角色,如果沒有,就會去匹配叫test的組角色,如果還沒有,那么tester用戶進入系統后的角色會是default(default角色在配置文件里面定義)。也就是說,tester用戶登錄后,不是tester用戶角色,就是test組角色,要么就是defaul
t缺省角色,不可能進入其他policy中定義的角色。當然,登錄之后可以切換角色,這需要配置相應的policy,參考角色切換一節的介紹。

定義角色的參數:

A – This role is an administrative role, thus it has special privilege normal roles do not have. In particular, this role bypasses the additional ptrace restrictions N – Don’t require authentication for this role. To access the role, use gradm -n <rolename> s – This role is a special role, meaning it does not belong to a user or group, and does not require an enforced secure policy base to be included in the ruleset. u – This role is a user role g – This role is a group role G – This role can use gradm to authenticate to the kernel A policy for gradm will automatically be added to the role. T – Enable TPE for this role l – Enable learning for this role P – Use PAM authentication for this role. 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

subject和object的定義

關於policy中subject和object的定義,那是相當的復雜,最好的辦法就是用gradm的學習模式自動生成策略。

角色的切換

在grsecurity系統中,角色的切換實際上就是用戶的切換,通過su命令,由test01用戶切換到test02用戶,那么你的角色就由test01切換到了test02。當然,有兩個前提,第一是你的policy是允許test01通過su命令進入test02的,否則根本
就且換不了用戶,更別提角色的變化了,第二是你的policy中定義了test02這個角色,否則,系統將會去匹配test02所在組的組角色,如果組角色也沒有定義,那么切換到test02后,你的角色就是default。

grsecurity的學習模式

由於人工編寫policy是非常復雜的,幾乎是不可能完成的,所以gradm為我們提供了一個很強大很好用的學習模式,以使系統能夠根據用戶的日常操作,來學習哪些操作是被允許的,哪些文件是可讀的,哪些文件是可寫的等等。使用學習模式有一個前提,就
是作為用戶,必須非常清楚哪些操作應該被允許,哪些操作不應該被允許。如果一個不應該被允許的操作,在學習模式開啟的情況下卻被執行了,那么gradm將會記錄該操作,並把他列為允許執行之列。
學習模式有兩種,分別是Process and role base learning(基於進程和角色的學習)和Full system learning(全系統學習)。下面分別介紹一下:

基於進程和角色的學習
該種學習模式可以指定某個角色或者某個角色中的特定進程進入學習模式

如果我們想讓test角色學習,就在test角色定義處添加”l”,如”role test ul”,這樣的話,只要你進入這個角色,之后所有你執行的命令都會被學習模式記錄下來。
如果只想讓test角色中的ls進程學習,就在ls的subject處添加”l”,如”subject /usr/bin/ls l”。
設置完后,執行gradm -D, 確認grsecurity關閉,然后執行 gradm -L /etc/grsec/learning.logs -E 進入學習模式。
我們以ls進程的學習為例,在學習模式下,切換到test角色(su test),然后執行設置了學習模式的命令”ls”,比如,想要”ls”對/etc/可讀,那就執行”ls /etc/”,想要ls對/mnt可讀,那就執行”ls /mnt”。當你需要做的所有操作都做了至少4遍以后,就可以執行以下命令生成policy。

gradm -D #先要停止grsecurity gradm -L /etc/grsec/learning.logs -O /etc/grsec/acl #生成acl文件
  • 1
  • 2

把acl文件中的內容復制粘貼到policy中對應的subject中。詳細操作參看學習模式應用實例。

全系統學習

該學習模式下,系統將記錄所有被policy文件deny的操作,並生成相應的policy,以保證這些被deny的操作在新的policy中不會被deny。
這種模式不需要修改配置文件,直接執行如下命令即可:
1)gradm -F -L /etc/grsec/learning.logs
2)執行你需要賦予權限的命令或操作至少4次
3)生成acl文件
gradm -F -L /etc/grsec/learning.logs -O /etc/grsec/acl
4)將acl中的內容復制粘貼到policy中。詳細的復制粘貼方法參看學習模式應用實例。

用戶切換后,角色也切換,角色的權限是不是繼承的?
答:不會繼承

PaX/Grsecurity 發布 RAP

2016年4月28日下午三點,PaX/Grsecurity正式公布了針對Linux內核4.5里的新特性:RAP。RAP是一種在Linux內核層面 上的CFI(控制流完整性)的實現,致力於完全消滅代碼重用攻擊這種漏洞利用的方式,RAP的發布是系統安全領域的又一里程碑,這意味着自2003年PaX team談“未來”至今,只剩下data-only attack並未完全解決,從技術選型,研究,開發和測試發布,PaX team一共歷經5年左右的時間,RAP第一次亮相是去年的H2HC, 這次公開發布的版本雖然只支持x86_64,沒有經過連接時優化,編譯時的靜態分析以及返回地址保護等重要feature,但足以適應於大部分的場景,公 開的版本是基於GPLv2自由軟件許可證發布的。另外PaX/Grsecurity的下一個穩定版選為4.4,3.14的穩定版會一直維護到2017年年底


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM