スマートフォン解析 top

TOP > タイガーチームセキュリティレポート > Berkeley Packet Filterの基礎と応用 - Part 3

タイガーチームセキュリティレポート

Berkeley Packet Filterの基礎と応用 - Part 3

今回と次回でBPFの応用例としてLinuxのseccompについて見ていきます。


-seccompの概要-

まずはseccompの機能と、現在の実装にいたるまでの経緯について紹介します。

seccomp(secure computing mode)はLinux Kernelにおけるsandbox機能の一種です。

seccompが採用しているsandboxのアプローチは、以前このコラムで紹介した
LXCなどが採用しているようなシステムリソースの仮想化によるアプローチではなく
プロセスを一部のシステムコールだけが許可された実行モードに拘束することにより
強制的にシステムリソースから隔離するものです。

seccompはAndrea ArcangeliのサイドビジネスであったCPUShareという
分散コンピューティングの一種から生まれました。(残念ながらサイトは閉鎖された模様:-0)
CPUShareでは余っているCPUの計算リソースを共有するために、
信頼出来ない他人のbyte codeを安全に実行できるような環境が必要でした。

www.cpushare.com


一般的にuser spaceのプログラムがシステムを攻撃するためには
kernel spaceへのentry pointとして何らかシステムコールをcallする必要があります。
seccompでは4つのシステムコールexit(), sigreturn(), read(), write()のみを
許可することでバイトコードが安全に実行される保証を与えます。

seccompのアプローチは非常にシンプルでそれゆえに美しくもあったわけですが
当時はCPUShare以外のアプリケーションには利用されていないと思われていました。[4]
後に、Linux版Google Chromeにseccompが利用されていることがわかりました。

chrome://sandbox


Google Chromeのセキュリティについて、機会があれば別のコラムで触れようと思います。

2005年にseccompがLinux Kernel 2.6.12にマージされてから
何度かseccompの拡張が試みられましたが、しばらくは上手く行かなかったようです。
2012年にBPFをシステムコールのフィルタに応用するアプローチが登場し、
めでたくLinux Kernel 3.5にマージされました。

Linux Kernel 3.5以降、seccompには2つのモードstrictとfilterがあり、
モードを切り替えて利用できるようになっています。

strictモードのseccomp(seccomp-legacy)に入ったプロセスは、
従来通り4つのシステムコールexit(), sigreturn(), read(), write()のみが許可された状態になります。
filterモードのseccomp(seccomp-bpf)ではフィルタの実行結果によって
システムコールが許可されるかどうかが決まります。

filterモードではシステムコールの種類だけでなくその引数まで評価できるので
アプリケーションごとの個別の要求にあわせて柔軟にsandbox環境を作ることができます。
seccomp-bpfはGoogle Chromeの他に、VsftpdやOpenSSHでも利用されてきており
今後は、より広く普及していくことが予想されます。

概要は以上です。つぎに、Linux Kernelのソースからseccompの実装を見てみましょう。


-seccomp-legacyの実装-

BPFの応用であるfilterモードについて見る前に、strictモードの実装を見ておきます。
参考にしたのはDebian wheezyのLinux Kernel 3.11です。
プロセスがseccompに入るためにはprctl()をcallします。以下は、プロトタイプです。

SYNOPSIS
       #include <sys/prctl.h>

       int prctl(int option, unsigned long arg2, unsigned long arg3,
                 unsigned long arg4, unsigned long arg5);

optionにPR_SET_SECCOMP、arg2にSECCOMP_MODE_STRICTを指定することで
strictモードのseccompに入ります。arg3、arg4、arg5に指定した値は、ここでは無視されます。(0でOK)

arg2で指定したmodeはprctl_set_seccomp()内で
prctl()を呼び出したプロセスのtask_struct構造体のseccompフィールドにセットされます。(509行目)

469 /**
470  * prctl_set_seccomp: configures current->seccomp.mode
471  * @seccomp_mode: requested mode to use
472  * @filter: optional struct sock_fprog for use with SECCOMP_MODE_FILTER
473  *
474  * This function may be called repeatedly with a @seccomp_mode of
475  * SECCOMP_MODE_FILTER to install additional filters.  Every filter
476  * successfully installed will be evaluated (in reverse order) for each system
477  * call the task makes.
478  *
479  * Once current->seccomp.mode is non-zero, it may not be changed.
480  *
481  * Returns 0 on success or -EINVAL on failure.
482  */
483 long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)
484 {
485         long ret = -EINVAL;
486
487         if (current->seccomp.mode &&
488             current->seccomp.mode != seccomp_mode)
489                 goto out;
490
491         switch (seccomp_mode) {
492         case SECCOMP_MODE_STRICT:
493                 ret = 0;
494 #ifdef TIF_NOTSC
495                 disable_TSC();
496 #endif
497                 break;
498 #ifdef CONFIG_SECCOMP_FILTER
499         case SECCOMP_MODE_FILTER:
500                 ret = seccomp_attach_user_filter(filter);
501                 if (ret)
502                         goto out;
503                 break;
504 #endif
505         default:
506                 goto out;
507         }
508
509         current->seccomp.mode = seccomp_mode;
510         set_thread_flag(TIF_SECCOMP);
511 out:
512         return ret;
513 }
kernel/seccomp.c

seccompに入ったあと、同じプロセスがシステムコールをcallすると
そのたびにsecure_computing()が実行されてそのシステムコールが許可されているかチェックされます。
チェックをパスしなかった場合、プロセスは強制的にKILLされます。

strictモードの場合だとcallしたシステムコールの番号が
許可されたシステムコールのホワイトリスト(mode1_syscalls)に含まれているかどうかを
チェックするだけです。392行目の前後がチェック処理にあたります。

360 /*
361  * Secure computing mode 1 allows only read/write/exit/sigreturn.
362  * To be fully secure this must be combined with rlimit
363  * to limit the stack allocations too.
364  */
365 static int mode1_syscalls[] = {
366         __NR_seccomp_read, __NR_seccomp_write, __NR_seccomp_exit, __NR_seccomp_sigreturn,
367         0, /* null terminated */
368 };
369
370 #ifdef CONFIG_COMPAT
371 static int mode1_syscalls_32[] = {
372         __NR_seccomp_read_32, __NR_seccomp_write_32, __NR_seccomp_exit_32, __NR_seccomp_sigreturn_32,
373         0, /* null terminated */
374 };
375 #endif
376
377 int __secure_computing(int this_syscall)
378 {
379         int mode = current->seccomp.mode;
380         int exit_sig = 0;
381         int *syscall;
382         u32 ret;
383
384         switch (mode) {
385         case SECCOMP_MODE_STRICT:
386                 syscall = mode1_syscalls;
387 #ifdef CONFIG_COMPAT
388                 if (is_compat_task())
389                         syscall = mode1_syscalls_32;
390 #endif
391                 do {
392                         if (*syscall == this_syscall)
393                                 return 0;
394                 } while (*++syscall);
395                 exit_sig = SIGKILL;
396                 ret = SECCOMP_RET_KILL;
397                 break;
kernel/seccomp.c

以上がstrictモードの動作になります。コンパクトで良いですね。:-)


-まとめ-

今回はLinuxのseccompを紹介しLinux Kernelのソースを眺めてみました。
seccomp-legacyの実装は驚くほど(?)シンプルになっていることが見て取れるかと思います。


参考情報:
[1] seccomp - Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Seccomp
[2] Linux 3.5 - Linux Kernel Newbies
http://kernelnewbies.org/Linux_3.5
[3] Seccomp and sandboxing [LWN.net]
http://lwn.net/Articles/332974/
[4] Re: [PATCH 2/2] x86-64: seccomp: fix 32/64 syscall hole [LWN.net]
http://lwn.net/Articles/332976/
[5] Yet another new approach to seccomp [LWN.net]
https://lwn.net/Articles/475043/
[6] Man page of PRCTL
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/prctl.2.html


次回は、seccomp-bpfの実装とフィルタの実例を見てみたいと思います。


タイガーチームメンバー 塚本 泰三