近年來由於 IoT 風潮帶來萬物皆連網, 但隨之而來的就是 “萬物皆可攻擊" 的安全議題
下圖 [1] 為 1999 ~ 2017 年 CVE (Common Vulnerabilities and Exposures) 統計 (CVE 收集各種資訊安全弱點及漏洞並供大眾查閱) 可以看到這幾年 CVE 數量明顯增加.
程式有安全漏洞在所難免, 即便開發者具備良好的程式技巧跟心態, 還是有機會出現安全漏洞, 導致系統被有心人士攻擊入侵. 所以事後的防護就很重要, seccomp 就是為此而產生
seccomp 設計宗旨:
指定 process 只能呼叫特定的 system call
以 Linux kernel 為例, 其提供的 system call 就有上百種 . 但大部份 system call 都不會被 process 所使用. 透過設定 seccomp 除了不影響原本執行結果, 也能降低一旦 process 遭受攻擊對整體系統造成的影響.
seccomp 在 Linux 上的實作:
首先需開啟 kernel 選項, 確保下列設定已選取
- CONFIG_SECCOMP=y
- CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
- CONFIG_SECCOMP_FILTER=y
seccomp 實作共分成兩種模式
1. Strict mode
√ kernel space:
若 process 設成 strict mode, 只能呼叫下列四種 system calls. 其他 system calls 會收到 SIGKILL signal
- read
- write
- _exit (不含 exit_group)
- sigreturn
相關實作在 kernel/seccomp.c 中. 如下圖所示
若 seccomp 模式為 strict, 則只會設定之前提到的四種 system calls 進白名單.
最後會呼叫位於 kernel/auditsc.c 中的 audit_seccomp 來進行 system-call auditing
√ user space:
可使用 prctl 或者 seccomp (kernel 版本需大於 3.17) 函式, 兩者等價, 皆可設定成 strict 模式
2. Filter mode [5]
√ kernel space:
在此模式下, 允許使用 BPF [8] 來設定黑名單或者白名單. 若白名單中允許 fork 以及 clone system call, 則此 process 的 child process 一律繼承 parent process 原有的 system call filters.
設定此模式 caller 需具備 CAP_SYS_ADMIN 的屬性或者 thread 必須設定 no_new_privs [9]. 若 thread 尚未設定, 可透過 prctl(PR_SET_NO_NEW_PRIVS, 1); 來啟動設定. 否則 SECCOMP_SET_MODE_FILTER 會回傳錯誤. 透過這些設定可確保無特權的 process 無法新增惡意 filters 並且執行 execve system call.
no_new_privs 是 kernel 3.5 之後才加入的功能, 一旦啟動設定, 則所有透過 fork, clone, 以及 execve 執行產生的 process 皆繼承 parent process 權限. 避免因為 execve 允許執行的程式權限大於 parent 而造成一些安全漏洞.
相關實作在 kernel/seccomp.c 中. 如下圖所示
√ user space:
可使用 prctl 或者 seccomp (kernel 版本需大於 3.17) 函式, 兩者等價, 皆可設定成 filters 模式
- seccomp(SECCOMP_SET_MODE_FILTER, 0, args);
- prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, args);
seccomp filter 回傳 32-bit 大小的值, 前 16-bit 為 SECCOMP_RET_ACTION, 後 16-bit 為 SECCOMP_RET_DATA
1. SECCOMP_RET_ACTION – kernel 執行特定行為
seccomp filter 則定義數種行為如下:
- SECCOMP_RET_KILL – 不執行 system call, 立即中止 process (SIGSYS)
- SECCOMP_RET_TRAP – 不執行 system call, process 送出 (SIGSYS) system call, 並且將 system call 相關資訊儲存至 siginfo_t [11]
- SECCOMP_RET_ERRNO – 不執行 system call, SECCOMP_RET_DATA 回傳 errno
- SECCOMP_RET_TRACE – 會驅動 ptrace base 的 tracer (如gdb), 讓 tracer 可以來接手處理. 若沒有相關 tracer 則回傳 -ENOSYS
- SECCOMP_RET_ALLOW – system call 正常執行
若同時符合多個條件, SECCOMP_RET_ACTION 則只會回傳優先權較高的值.
2. SECCOMP_RET_DATA – 回傳值
Filter mode 下的寫法複雜許多, 需要瞭解並撰寫 BPF instructions. 可參考 kernel source: samples/seccomp/ [10] 底下, 有許多 seccomp filter 相關範例.
查詢 Process 是否已設定 seccomp:
可透過 /proc/<pid>/status 來查詢該 process 的 seccomp 模式
- 0: 尚未啟動
- 1: 啟用 “STRICT" 模式
- 2: 啟用 “FILTER" 模式
如下圖所示, 此 process 尚未啟動 seccomp
使用時機:
√ 建置沙盒
建置沙盒 (sandbox) 的隔離環境, 限定隔離環境中的應用程式只能執行特定 system calls, 避免應用程式遭攻擊時影響整個系統.
√ 建立測試環境
可用來建立測試應用程式環境, 限定隔離環境中的應用程式只能執行特定 system calls, 避免應用程式錯誤發生而影響整個系統
注意事項:
使用 filter mode 時, 須注意實際機器的 CPU architecture . 因為不同 CPU 架構的 system call number 可能相異, 一不注意會造成問題.
心得:
“Software Bugs Will Always Exist"
使用 seccomp 來限制特定 process 的權限, 即便程式出了問題, 也能將整體系統的傷害降到最低. 不過 BPF 的寫法較為複雜, 這邊要小心處理, 避免 filter 無法作動.
References:
[1]: http://www.cvedetails.com/browse-by-date.php
[2]: http://elixir.free-electrons.com/linux/latest/source/arch/arm/tools/syscall.tbl
[3]: http://kernsec.org/files/lss2015/seccomp.pdf
[4]: http://man7.org/conf/lpc2015/limiting_kernel_attack_surface_with_seccomp-LPC_2015-Kerrisk.pdf
[5]: https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt
[6]: https://lwn.net/Articles/656307/
[7]: http://man7.org/linux/man-pages/man2/seccomp.2.html
[8]: https://www.kernel.org/doc/Documentation/networking/filter.txt
[9]: https://www.kernel.org/doc/Documentation/prctl/no_new_privs.txt
[10]: http://elixir.free-electrons.com/linux/latest/source/samples/seccomp/
[11]: http://man7.org/linux/man-pages/man2/sigaction.2.html
seccomp 學習筆記 有 “ 1 則迴響 ”