快速SECCOMP入門
為什么不能只使用LSM?
為什么不能只使用seccomp?
結論
假設你已經了解了LSM內核安全模塊,也知道如何使用它們加固系統的安全。但是,你還知道了另一種工具seccomp(Linux安全計算)。你可能非常想知道,LSM和Seccomp有什么區別?為什么不能將Seccomp設計為LSM模塊?什么時候使用Seccomp?接下來,且聽我娓娓道來。
Secomp和LSM都可以讓內核限制進程與系統的交互,但卻有大大的不同。也就是說,Seccomp限制進程發起的系統調用,而LSM控制對內核對象的訪問。
快速SECCOMP入門
Andrea Arcangeli在2004年首次提案seccomp,限制用戶進程進入內核模式的系統調用,只允許read、write、exit和sigreturn。Andrea將其用于CPUShare項目,其允許人們出租自己未使用的CPU資源,但這需要限制不可信代碼的行為。出售者肯定不想購買者能夠輕易的通過fork bomb發起拒絕服務攻擊(DoS)。起初,Andrea使用一個運行時受限的解釋器運行不可信代碼,但是為了效率和靈活性,他專門設計了CPUShare用來直接運行二進制代碼。CPUShare需要限制這種不可信的二進制代碼,以便只能讀取輸入,運算,然后輸出結果。除此之外,不允許任何操作。有了Seccomp,管理進程能夠加載不可信的共享對象文件,打開文件進行輸入和輸出,并進入seccomp模式,然后,才調用不可信共享對象的入口點,運行二進制代碼。之后,受限的進程不能打開任何新文件,更改目錄,創建新進程,生成線程,執行新程序。
seccomp的這種strict模式帶來了很強的隔離性,但是限制太多而無法更普遍的使用。那么就需要一種方法為進程提供策略給內核決定允許那些系統調用。2012年,內核開發者合并了seccomp filter mode,從而允許進程決定它們自己的系統調用過濾策略。
Seccomp filter mode允許進程通過prctl系統調用安裝BPF字節碼。一旦安裝,此BPF程序阻止調用進程,或任何子進程發起的系統調用。
讓我們來看一下Linux源碼中seccomp測試代碼片段,如下所示。該段代碼展示了BPF過濾器會殺死發起getpid系統調用的進程:
?
struct sock_filter filter[] = { BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, nr)), BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1), BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL), BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), }; struct sock_fprog prog = { .len = (unsigned short)ARRAY_SIZE(filter), .filter = filter, }; ... ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
?
上面的代碼創建了一個seccomp過濾器。一旦將該過濾器添加到某個任務中,在對任務進程進行追蹤之后,但是在通過系統調用表分配之前,會先運行該過濾器,從而限制某些系統調用。值得注意的是seccomp_data數據結構,如下所示:
?
/** * struct seccomp_data - BPF程序執行的格式 * @nr: 系統調用號 * @arch: 系統調用的約定,跟架構相關,相關定義位于 *文件中,以AUDIT_ARCH_*為前綴 * @instruction_pointer:指令指針 * @args: 6個系統調用參數,64位值,不管是什么架構 */ struct seccomp_data { int nr; __u32 arch; __u64 instruction_pointer; __u64 args[6]; };
?
目前,BPF沒有提供解引用指針,或檢查用戶空間傳遞進來的數據結構。只有直接拷貝到seccomp_data結構中的參數才可用。因此,BPF過濾器不能通過用戶空間傳遞的字符串確定系統調用。
為什么不能只使用LSM?
LSM和seccomp都是增加系統安全的工具。LSM實現的是強制訪問控制(MAC),保護的內核對象是:文件,inode,task_struct,IPC數據結構。LSM會將安全屬性插入到這些對象中,根據先前加載的策略進行檢查。具有特權的安全管理員(通常是root)負責加載和管理這些策略。非特權用戶不能改變這個策略。
相反,seccomp允許非特權進程限制自身。與非特權進程放棄自身能力(capability)一樣,seccomp允許非特權進程放棄某些系統調用的能力。比如說,cat命令,可以open和read命令行中指定的文件,將它們的內容輸出到標準輸出中。除了stdout(2)和stderr(3)之外,它不需要寫任何文件描述符。因此,cat開發者可以使用seccomp過濾器,當cat想要寫任何除了2和3之外的文件描述符時,就會殺死它。
但是,通過LSM實現相同的功能就會非常復雜,因為進程的標準輸出可能會重定向到不同內核對象(設備,文件,管道),而LSM本身又沒有提供將這些對象映射到文件描述符的方法。另外,一個問題就是存在多個LSM實現:SELinux,?AppArmor,?Tomoyo,?SMACK,它們都有自己的管理策略的機制,這些機制要么要為每一個LSM模塊生成策略,要么限制用戶只能使用一種。
而且,還可以編寫自己的LSM模塊,當然,還需要將其編譯進內核,并設計一種方法表示你這種特殊的策略,這既耗時又脆弱。在這一點上,seccomp是有優勢的,如果內核配置正確,不管系統如何配置其LSM模塊,都可以使用它。從二進制的角度來說,
最后,因為內核會在系統調用之前檢查seccomp過濾器,也就是減少了攻擊者的攻擊面。LSM一般hook在系統調用底層的內核對象上,不會像seccomp那樣減少攻擊面。具體的來說,內核在將系統調用的參數映射到內核對象之后運行這些hook,本身這段代碼可能就包含缺陷。重申一遍:seccomp減少攻擊面。
為什么不能只使用seccomp?
seccomp無法對LSM采用的相同策略進行建模。例如,你不能編寫seccomp過濾器阻止用戶進程只打開文件系統中某個位置的文件,比如,/etc/password。因為seccomp過濾器不能解引用指針,所以它就不能比較用戶傳遞給open系統調用的路徑參數(像AppArmor那樣),也不能檢查通過文件安全屬性檢查inode節點(像SELinux那樣)。
另外,也沒有機制移除或修改已經附加到進程的過濾器。你只能給進程添加新的過濾器,而且新過濾器的作用也不能與已經存在的過濾器相反。因此,想要從全局給系統創建一組過濾器是非常困難的。最接近的方法是使用systemd的系統調用過濾器或者在容器中使用seccomp過濾器(比如docker)
即使使用systemd的系統調用過濾器和docker seccomp策略,seccomp過濾器的管理最好還是由應用程序開發者實現,而不是系統管理員。因為應用開發者比系統管理員更清楚他們的應用程序需要哪些系統調用。另外,在應用程序改變時,開發者更清楚在哪里更新這些過濾器。
結論
LSM和seccomp都提供了限制進程與系統交互的機制。但它們不是不直接保護你的程序免受攻擊的工具,而是阻止攻擊者利用某個程序的漏洞進而攻擊系統其它部分的工具。LSM實現的MAC強制訪問控制策略是你實現系統全局細粒度安全控制策略的工具,而seccomp過濾器是限制非特權進程進行某些系統調用的工具,同時還是常見的進程沙箱技術(如linux container)的重要組件。
審核編輯:湯梓紅
評論
查看更多