web-dev-qa-db-ja.com

サンドボックスはどのように実装されていますか?

私が知りたいのは2つあります。最初に、サンドボックスとは何ですか。それはOSシステムコールのトラップであり、次にそれが通過することを許可するかどうかを決定していますか?そもそもどのように実装されていますか? SSDT(カーネルレベル)のフックによるものでしょうか?

19
David J

さて、この回答はかなり長くなりましたが、サンドボックス化は大きなトピックです。サンドボックス化は、最も基本的には、悪意や誤動作が発生した場合にプログラムが他のシステムに与える影響を最小限に抑える手法です。これは、システムのテストまたはセキュリティの強化のために使用できます。サンドボックスを使用する理由もさまざまであり、場合によっては、セキュリティに関連しないこともあります。たとえば、OpenBSDの systrace の場合などです。サンドボックスの主な用途は次のとおりです。

  • 特にビルド時に、壊れたパッケージを検出するためのプログラムテスト。
  • 悪意のあるソフトウェアの動作を理解するためのマルウェア分析。
  • 信頼できない、または安全でないアプリケーションを保護して、実行できる損害を最小限に抑える。

多くのサンドボックス化手法があり、すべて脅威モデルが異なります。使用できるAPIを制限することで攻撃対象領域を減らすだけのものもあれば、 Bell-LaPadula または Biba と同様の形式化されたモデルを使用してアクセス制御を定義するものもあります。主にLinux向けのいくつかの一般的なサンドボックス手法について説明しますが、他のオペレーティングシステムについても触れます。

Seccomp

Seccomp は、カーネルの攻撃対象領域を削減するLinuxセキュリティ機能です。これは技術的にはサンドボックスではなく、syscallフィルターですが、サンドボックスを拡張するためによく使用されます。 seccompフィルターには、モード1(厳密モード)とモード2(eBPFモード)の2種類があります。

モード1

Seccompモード1は、最も厳密なオリジナルのモードです。プログラムがモード1のseccompを有効にする場合、使用できるのは4つだけに制限されます ハードコードされたsyscallsread()write()exit()、および rt_sigreturn() 。 seccompを適用する前に、必要なファイル記述子を作成する必要があります。違反の場合、問題のプロセスはSIGKILLで終了します。

バイトコードで42を返す関数を安全に実行するサンプルプログラムである別のStackExchangeサイトに書いた別の回答から引用します。

_#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <linux/seccomp.h>

/* "mov al,42; ret" aka "return 42" */
static const unsigned char code[] = "\xb0\x2a\xc3";

void main(void)
{
    int fd[2], ret;

    /* spawn child process, connected by a pipe */
    pipe(fd);
    if (fork() == 0) {
        close(fd[0]);

        /* enter mode 1 seccomp and execute untrusted bytecode */
        prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);
        ret = (*(uint8_t(*)())code)();

        /* send result over pipe, and exit */
        write(fd[1], &ret, sizeof(ret));
        syscall(SYS_exit, 0);
    } else {
        close(fd[1]);

        /* read the result from the pipe, and print it */
        read(fd[0], &ret, sizeof(ret));
        printf("untrusted bytecode returned %d\n", ret);
    }
}
_

モード2

モード2 seccomp(seccomp-bpfとも呼ばれます)には、ユーザースペースが作成したポリシーが含まれます カーネルに送信されます 、許可されるsyscalls、それらのsyscallsに許可される引数、および実行されるアクションsyscall違反の場合。フィルターは eBPF バイトコードの形式で提供されます。これは、カーネルで解釈され、フィルターの実装に使用される特殊なタイプの命令セットです。これは、たとえば、Linuxの Chrome/Chromium および OpenSSH サンドボックスで使用されます。

Seccomp-bpfを使用して現在のPIDを出力する単純なプログラム:

_#include <seccomp.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

void main(void)
{
    /* initialize the libseccomp context */
    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);

    /* allow exiting */
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);

    /* allow getting the current pid */
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);

    /* allow changing data segment size, as required by glibc */
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);

    /* allow writing up to 512 bytes to fd 1 */
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 2,
        SCMP_A0(SCMP_CMP_EQ, 1),
        SCMP_A2(SCMP_CMP_LE, 512));

    /* if writing to any other fd, return -EBADF */
    seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EBADF), SCMP_SYS(write), 1,
        SCMP_A0(SCMP_CMP_NE, 1));

    /* load and enforce the filters */
    seccomp_load(ctx);
    seccomp_release(ctx);

    printf("this process is %d\n", getpid());
}
_

Linux syscall ABI は引数を汎用レジスターに保持するため、これらのレジスターのみがseccompによって検証されます。これは、引数がフラグのビット単位のORされたリストである場合など、一部のケースでは問題ありませんが、引数がメモリへのポインタである場合、フィルタリングは機能しません。これは、ポインタがメモリを参照するだけなので、ポインタを検証すると、ポインタ自体が許可されていることのみが確認され、参照しているメモリが変更されていないことが確認されないためです。これは、open()(パスがメモリ内のnullで終了する文字列へのポインターである)のようなsyscallの特定の引数を確実にフィルターすることができないことを意味します。パスおよび類似のオブジェクトをフィルターに掛けるには、必須のアクセス制御または別のLSMベースのフレームワークを使用する必要があります。

誓約

OpenBSDは、 pledge (以前はtame)と呼ばれるseccompに似た(しかしより粗い)syscallフィルターを追加しました。 Pledgeは、アプリケーションがオプトインできるsyscallであり、基本的にはさまざまなカーネルインターフェイスの使用を制限する「プレディング」です。アプリケーションがどれだけ要求したとしても、カーネルは、いったん配置されると、制限を取り消すことはありません 気が変わっても

誓約により、アプリケーションは約束を行うことができます。これは、許可されるアクションのグループであり、他のすべては拒否されます。本質的には、事前に明示的に要求した機能のみを使用することが約束されています。いくつかの(完全ではない)例:

  • stdioプロミスは、ファイル記述子のクローズやメモリの管理などの基本的な機能を許可します。
  • rpathpromiseは、ファイルシステムの読み取りに使用できるsyscallsを許可します。
  • wpathpromiseは、ファイルシステムへの書き込みに使用できるsyscallsを許可します。
  • tmppathpromiseは、読み取り/書き込みができるシステムコールを許可しますが、_/tmp_でのみです。
  • idプロミスは、setuid()のように、資格情報を変更するために使用されるsyscallsを許可します。

誓約はseccompよりもはるかに大まかですが、このため、使用および保守がはるかに簡単です。このため、OpenBSDは、sshdのようなセキュリティ上重要なものからcatのような些細なものまで、さまざまな基本アプリケーションに誓約のサポートを追加するようになりました。この「デフォルトでのセキュリティ」アーキテクチャは、個々のプロミスが粒度が粗く、特に柔軟性がない場合でも、システム全体のセキュリティを大幅に改善することになります。

Chroot

chroot は、指定されたプログラムのルートディレクトリとして新しいパスを設定できる* nix機能であり、そのパスを基準としてすべてを強制的に表示します。特権プログラムは多くの場合 chrootをエスケープする であり、IPCまたはネットワークを分離しないため、これは通常セキュリティには使用されません。特権のないプロセスでもkillのような悪用を行うことができます。他のプロセス。少し触れれば、他のセキュリティ技術を強化するために使用できます。これは、アプリケーションが偶発的な損傷を与えるのを防ぎ、レガシーソフトウェアが期待するファイルシステムのビューを提供するのに非常に役立ちます。

たとえば、bashをChrootするには、必要な実行可能ファイルとライブラリを新しいディレクトリに配置し、chrootユーティリティを実行します(それ自体が 同じ名前のsyscallを呼び出すだけです)。 ):

_Host ~ # ldd /bin/bash
        linux-vdso.so.1 (0x0000036b3fb5a000)
        libreadline.so.6 => /lib64/libreadline.so.6 (0x0000036b3f6e5000)
        libncurses.so.6 => /lib64/libncurses.so.6 (0x0000036b3f47e000)
        libc.so.6 => /lib64/libc.so.6 (0x0000036b3f0bc000)
        /lib64/ld-linux-x86-64.so.2 (0x0000036b3f938000)
Host ~ # ldd /bin/ls
        linux-vdso.so.1 (0x000003a093481000)
        libc.so.6 => /lib64/libc.so.6 (0x000003a092e9d000)
        /lib64/ld-linux-x86-64.so.2 (0x000003a09325f000)
Host ~ # mkdir -p newroot/{lib64,bin}
Host ~ # cp -aL /lib64/{libreadline,libncurses,libc}.so.6 newroot/lib64
Host ~ # cp -aL /lib64/ld-linux-x86-64.so.2 newroot/lib64
Host ~ # cp -a /bin/{bash,ls} newroot/bin
Host ~ # pwd
/root
Host ~ # chroot newroot /bin/bash
bash-4.3# pwd
/
bash-4.3# ls
bin  lib64
bash-4.3# ls /bin
bash  ls
bash-4.3# id
bash: id: command not found
_

_CAP_SYS_CHROOT_ capability のプロセスのみがchrootに入ることができます。これは、悪意のあるプログラムが制御するディレクトリに_/etc/passwd_の独自のコピーを作成し、suのようなsetuidプログラムを使用してchrootし、バイナリをだましてrootにすることを防ぐために必要です。

名前空間

Linuxでは、名前空間はシステムリソースを分離するために使用され、名前空間を持つプログラムが所有するリソースについて異なる理解を提供します。これは一般的にコンテナを実装するために使用されます。 namespaces(7) マンページから:

名前空間は、グローバルシステムリソースを抽象化してラップし、グローバルリソースの独自の独立したインスタンスを持っているという名前空間内のプロセスから見えるようにします。グローバルリソースへの変更は、名前空間のメンバーである他のプロセスからは見えますが、他のプロセスからは見えません。

現在、Linuxでは7つの名前空間がサポートされています。

  • cgroup -Cgroupルートディレクトリ
  • IPC-System V IPCおよびPOSIXメッセージキュー
  • ネットワーク-ネットワークインターフェイス、スタック、ポートなど
  • Mount -機能的にはchrootと同様のマウントポイント
  • [〜#〜] pid [〜#〜] -プロセスID
  • ser -ユーザーおよびグループID
  • UTS-ホスト名とドメイン名

unshare ユーティリティを使用したPID名前空間の例:

_Host ~ # echo $$
25688
Host ~ # unshare --fork --pid
Host ~ # echo $$
1
Host ~ # logout
Host ~ # echo $$
25688
_

これらを使用してサンドボックスを強化したり、サンドボックスの不可欠な部分として使用したりすることもできますが、セキュリティを低下させるものもあります。ユーザーの名前空間は、特権がない場合(デフォルト)、カーネルからはるかに大きな攻撃対象領域を公開します。 多数カーネル脆弱性are悪用可能 ユーザーの名前空間が有効になっている場合、非特権プロセスによって。一部のカーネルでは、_kernel.unprivileged_userns_clone_を0に設定することにより、非特権ユーザーの名前空間を無効にできます。

必須のアクセス制御

MACは、プログラムが実行できることと実行できないことをホワイトリストに基づいて定義するためのフレームワークです。プログラムはsubjectとして表されます。ファイル、パス、ネットワークインターフェイス、ポートなど、プログラムが処理したいものはすべて、オブジェクトとして表されます。オブジェクトにアクセスするためのルールは、permissionまたはフラグと呼ばれます。コメントを追加して、pingユーティリティの AppArmor ポリシーを取得します。

_#include <tunables/global>

/bin/ping {
  # use header files containing more rules
  #include <abstractions/base>
  #include <abstractions/consoles>
  #include <abstractions/nameservice>

  capability net_raw,  # allow having CAP_NET_RAW
  capability setuid,   # allow being setuid
  network inet raw,    # allow creating raw sockets

  /bin/ping mixr,      # allow mmaping, executing, and reading
  /etc/modules.conf r, # allow reading
}
_

このポリシーを設定すると、pingユーティリティが危険にさらされた場合、ホームディレクトリからの読み取り、シェルの実行、新しいファイルの書き込みなどを行うことができなくなります。この種のサンドボックスは、サーバーまたはワークステーションを保護するために使用されます。 AppArmor以外の一般的なMACには、 SELinux[〜#〜] tomoyo [〜#〜] 、および [〜#〜] smack [〜#が含まれます。 〜] 。これらは通常、カーネルに Linux Security Module 、またはLSMとして実装されます。これは、Linuxのサブシステムであり、セキュリティ情報を適用できるように、さまざまなアクション(資格情報の変更やオブジェクトへのアクセスなど)に hooks をモジュールに提供します。

ハイパーバイザー

ハイパーバイザーは仮想化ソフトウェアです。通常、これは ハードウェア機能 を利用して、CPUコア、メモリ、ハードウェアなどのすべてのシステムリソースを分離できます。仮想化システムは、ルートがあるだけでなく、リング0があると信じています(カーネルモード) 。ハードウェアは、CPUによって抽象化されるか(CPUコアとメモリ自体の場合)、ハイパーバイザーソフトウェアによってエミュレートされます(NICなどのより複雑なハードウェアの場合)。ゲストはシステム全体を所有していると信じ込まされているため、そのアーキテクチャで実行できるものはすべて仮想マシンでも実行される傾向があり、LinuxゲストとWindowsゲスト、またはFreeBSDホストとSolarisゲストを使用できます。 理論では、ハイパーバイザーはゲストのアクションがホストに影響するのを防ぎます。

ゲストのセットアップ方法を低レベルで理解するのに役立つ便利なリソースは、 KVM API のLWN書き込みです。 KVMは、Linuxでサポートされているカーネルインターフェイスで、仮想マシンをセットアップするための Illumos (Solarisのオープンソースフォークのファミリ)です。それはかなり低いレベル(_/dev/kvm_文字デバイスに対して開かれたファイル記述子のIOCTLを介してのみ行われる相互作用)であるため、通常は [〜#〜] qemu [〜#〜]のようなプロジェクトのバックエンドです。 。 KVM自体は、Intelプロセッサでは VT-x 、AMDでは AMD-V などの特権ハードウェア仮想化機能を利用します。

マルウェアの分析を支援するために、人気のある Cuckooサンドボックス などのハイパーバイザーが使用されることはよくあります。マルウェアを実行できる仮想マシンを作成し、システムの内部状態を分析し、メモリの内容を読み取ったり、メモリをダンプしたりします。完全なオペレーティングシステムを実行するため、マルウェアがそれを実現することはしばしば困難です。仮想化された実行。一部のマルウェアは仮想化の検出を試みることができますが(高度なレベルが異なる)、ダミーデバッガーをそれ自体に接続してデバッグできないような一般的な手法はだまされる可能性があります。ハイパーバイザーの検出自体は、非常に幅広く複雑なテーマです。

Qubes OS または Bromium (-- Xen hypervisor を使用して、それぞれFedoraとWindowsを分離するなど)によって、ハイパーバイザーはセキュリティのために(ab)使用されることがよくあります。 )。 Xenのバグが繰り返し発生するため、これが良いアイデアかどうかはよく議論されます。 OpenBSDの創設者であるTheo de Raddtによる、セキュリティに依存している仮想化のトピックに関する、有名でかなりひどい引用:

セキュリティホールのないオペレーティングシステムやアプリケーションを書くことができない世界中のソフトウェアエンジニアの集団が好転し、セキュリティホールのない仮想化レイヤーを突然書くことができると考えるなら、あなたはまったく愚かではありません。

ハイパーバイザーがセキュリティに適しているかどうかは、多くの要因に依存します。ゲストオペレーティングシステム全体を分離するため、通常は使用と保守が簡単ですが、攻撃対象領域が大きく、きめ細かな保護が提供されません。

コンテナ

コンテナーはハイパーバイザーに似ていますが、仮想化を使用するのではなく、名前空間を使用します。各コンテナは、独自の名前空間に置かれたすべてのリソースを持ち、すべてのコンテナが独立したオペレーティングシステムを実行できるようにします。コンテナーのinitプロセスは、それ自体をルートとして実行されているPID 1と見なしますが、ホストはそれを別の非初期および非ルートPIDと見なします。ただし、ホストのカーネルを共有しているため、ホストと同じタイプのオペレーティングシステムしか実行できません。さらに、コンテナには、ネットワークインターフェースのセットアップなどの特権アクションを実行できるルートプロセス(もちろん、そのコンテナのネームスペースのみ)を含めることができますが、すべてのコンテナに影響するグローバルカーネル設定を変更することはできません。 Docker および OpenVZ は、一般的なコンテナー実装です。

コンテナーは基本的にさまざまな実装のユーザー名前空間(Dockerの標準Linux名前空間、およびOpenVZのカスタムの名前空間テクノロジー)に依存しているため、 セキュリティが不十分 に対して批判されることがよくあります。これらのシステムでは、コンテナエスケープと権限昇格の脆弱性は珍しくありません。これらのセキュリティ問題の理由は、名前空間のrootユーザーが、新しく予期しない方法でカーネルと対話できるという事実に由来します。カーネルは、ネームスペースのルートユーザーがネームスペースに保持できないシステムに明らかに危険な変更を加えることができないように設計されていますが(sysctlの微調整など)、非特権よりも多くのカーネルと対話できます。処理する。このため、たとえば仮想ネットワークインターフェースのセットアッププロセスでrootのみが悪用できる脆弱性は、そのプロセスがユーザーの名前空間に入ることができる場合、非特権プロセスによって悪用される可能性があります。これは、syscallがコンテナのネットワークインターフェースのみを「参照」できる場合でも問題です。

結局のところ、ユーザー名前空間は、権限のないユーザーがカーネルのfarと対話することを許可するだけです。表面積は、さもなければ比較的無害である多くの脆弱性が代わりにLPEになる程度まで増加します。これは、カーネル開発者がrootしか対話できないコードを作成するときにセキュリティの考え方を守らない傾向がある場合に起こります。

その他の技術

サンドボックス機能を備えたオペレーティングシステムは、Linuxだけではありません。他の多くのオペレーティングシステムには独自のテクノロジーがあり、さまざまな方法で実装され、さまざまな脅威モデルを備えています。

  • AppContainer Windowsでは、chrootと名前空間の組み合わせに似た分離を提供します。ドメイン、ファイル、ネットワーク、さらにはウィンドウも分離されています。

  • Seatbelt OSXでは、必須のアクセス制御として機能し、制限されたアプリケーションがアクセスできるリソースを制限します。 bypasses のシェアを確認しました。

  • 刑務所 FreeBSDではchrootの概念に基づいて構築されています。刑務所で実行されているプログラムにIPアドレスを割り当て、独自のホスト名を与えます。 chrootとは異なり、セキュリティのために設計されています。

  • ゾーン Solarisは、ホストのカーネルの下でSolarisのユーザーランドのコピーを実行する高度なコンテナです。これらはFreeBSD Jailsに似ていますが、機能が豊富です。

39
forest

サンドボックス化についての Wikipediaページの概要

コンピューターのセキュリティでは、サンドボックスは、通常、システム障害やソフトウェアの脆弱性の拡散を緩和するために、実行中のプログラムを分離するためのセキュリティメカニズムです。多くの場合、ホストマシンやオペレーティングシステムに危害を加えることなく、未検証または信頼できないサードパーティ、サプライヤー、ユーザー、またはWebサイトから、テストされていない、または信頼できないプログラムまたはコードを実行します。サンドボックスは通常、ディスクやメモリのスクラッチスペースなど、ゲストプログラムが実行するための厳密に制御されたリソースのセットを提供します。ネットワークアクセス、ホストシステムを検査する機能、または入力デバイスから読み取る機能は、通常、許可されていないか、厳しく制限されています。

高度に制御された環境を提供するという意味では、サンドボックスは仮想化の特定の例と見なされる場合があります。サンドボックスは、ソフトウェアがホストデバイスに損害を与えることを許可せずに、ウイルスまたは他の悪意のあるコードを含む可能性がある未検証のプログラムをテストするために頻繁に使用されます。

つまり、サンドボックスは、信頼できないアプリケーション/タスク/コンピューター上で実行できるすべてのものを実行/分析するための制御および制限された環境を作成する実装です。

それらは実際に物理システムのようなものであり、簡単に設定および監視できるため、ほとんどが仮想マシンで実装されます。

別の方法は、同じウィキペディアのページで言及されているセキュアコンピューティングモード(seccomp)です。

Linuxカーネルに組み込まれたサンドボックス。 seccompをアクティブにすると、write()read()exit()、およびsigreturn()システムコールのみが許可されます。

オペレーティングシステムレベルの仮想化に関するウィキペディアのページから引用

次に、よく知られているオペレーティングシステムレベルの仮想化(コンテナー化とも呼ばれます)があります。カーネルが複数の分離されたユーザー空間インスタンスの存在を許可するオペレーティングシステム機能。コンテナ、パーティション、仮想化エンジン(VE)または刑務所(FreeBSD刑務所またはchroot刑務所)と呼ばれるそのようなインスタンス。それらは、実行されているプログラムの観点からは、実際のコンピューターのように見える場合があります。一般の人のコンピューターのオペレーティングシステムで実行されているコンピュータープログラムは、そのコンピューターのすべてのリソース(接続されたデバイス、ファイルとフォルダー、ネットワーク共有、CPUパワー、定量化可能なハードウェア機能)を見ることができます。ただし、コンテナー内で実行されているプログラムは、コンテナーのコンテンツと、コンテナーに割り当てられたデバイスしか表示できません。

Javaランタイム環境など、サンドボックスを使用する他の方法とアプリケーションがいくつかあります。サンドボックスと他の仮想化手法は、最終リリース前にアプリと環境をテストするために多く使用され、少ないハードウェアで多くのタスクを同時に実行します、マルウェアなどを分析します。用途とユーザビリティの範囲について簡単に理解していただければ幸いです。以下で必要なものがある場合は、遠慮なく質問してください。

1