スマートフォン解析 top

TOP > タイガーチームセキュリティレポート > Sandboxについて - Part 4

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

Sandboxについて - Part 4

sandboxに関するコラムの第4回です。今回もLXCのセキュリティを見ていきます。


-LXCのnetwork設定-

これまでのところLXCのnetwork設定については全く触れてきませんでした。

既定の状態ではContainerはHost環境とNetwork Namespaceを分離していないため
Container内からHost環境と同じnetwork環境に直接アクセスすることができます。

例えばmylxc内からifconfigでHost環境のnetwork設定を変更したり
tcpdumpでnetwork通信を盗聴することなども、可能です。



これらの動作はContainerからCAP_NET_*系のcapabilityをすべてdropすることでも
制限はできますが、Container内のプロセスの権限を必要以上に制限することは避けたいので
Host環境からNetwork Namespaceを分離することで対処することにします。


-network設定の実例-

mylxcにnetwork設定を追加してみます。
LXCではいくつかのnetworkのタイプから選ぶことができますが、ここではOpenVZ由来の仮想network技術であるvethを使ってHost環境とmylxcのNetwork Namespaceを分離します。

以下のような構成を設定します。


まずHost環境にbridge interfaceを追加し、IPアドレスを付与します。


次にlxc.confにvethの設定を追加します。
最後の行に追加した設定は後でmylxc内でsshdを立ち上げるときのための設定で、
sshで接続した際にsshdがptsを利用できるようにします。


以下は、ふたたびmylxcを立ち上げた後にHost環境とmylxcでifconfigを実行した結果です。

Host環境でifconfigを実行

mylxc内でifconfigを実行

Host環境とmylxcはともにeth0というnetwork interfaceを持っていますが、異なる設定になっていることがわかります。実はmylxcのeth0の実体はHost環境から見えるveth59Pnloというnetwork interfaceで、これがbr0を通して外部networkとbridgeされています。

この状態ではHost環境とmylxcの間で自由に通信できる状態なので、mylxcからHost環境をnetwork越しに攻撃することができてしまいます。

mylxcからHost環境への通信をnetfilterで制限します。Host側でiptablesを実行し、INPUT chainにルールを追加します。Network Namespaceが分離されているのでHost側のnetfilterルールとmylxcのnetfilterルールは独立して設定されます。


これで、mylxcからHost環境への通信は制限されました。念のためmylxcからHost環境のsshdに接続を試み、rejectされることを確認しておきます。


残念ながらこれでもまだ不十分です。mylxc内でnetfilterに関連するLKM(Loadable Kernel Module)を強制的にunloadしてしまえば良いのです。

使用中のLKMを強制unloadするにはkernelがCONFIG_MODULE_FORCE_UNLOADオプションを有効にしてbuildされている必要がありますが、wheezyではこれが有効になっています。


実際にiptable_filterとip_tablesをmylxc内からunloadして、Host環境のsshdに接続できることを確認してみます。 Host環境で設定したnetfilterのルールが消えてなくなっています。


mylxc内でLKMをloadすることもできます。先ほどunloadしたiptable_filterとip_tablesをloadしてみます。



-Containerからの脱出-

Container内からLKMを自由にload & unloadできるということは、
システムに対してほとんどあらゆる操作(攻撃)を実行可能であることを意味します。
例として、LKMを使用してmylxc内で実行中のkshをmylxcの外に脱出させてみます。
kshにしたのは単に目立つようにしたという以外に深い意味はありません。

前回のコラムでContainer外のfile systemにアクセスする方法を示しました。
しかしContainer外のfile systemにアクセスできるだけでは
Namespaceが分離されたままですので、セキュリティ的には制限が残っています。
例えばproc file systemからContainer外のプロセスに関する情報を見たり、
Container外のプロセスにシグナルを送ったりすることはできませんでした。

前々回のコラムで触れたようにuser spaceからNamespaceを操作するには
clone(), unshare(), setns()といったsystem callのどれかを呼び出す必要があり、
LXCもこれらのsystem callを利用しています。

Namespaceに関連する部分のLinux Kernelのソースをチェックしてみましょう。

OSの最も基本的な概念の一つであるプロセスはLinux Kernel上で
task_struct構造体で表されますが、この構造体にはnsproxyという名前のフィールドがあり
これは当該プロセスが属するNamespaceを表す構造体であるnsproxy構造体への
pointerとなっています。

include/linux/sched.hより抜粋


nsproxy構造体の定義を見てみると、Linux Kernel 3.2は
UTS, IPC, Mount, PID, Netの5つのNamespaceをサポートしていることがわかります。

include/linux/nsproxy.hより抜粋


プロセスが同じNamespaceに属するということは同じnsproxy構造体を持つということです。
また、Namespaceを操作するということはこれらのデータ構造を操作することを意味します。
さらにsetns()のソースコードを覗いてみましょう。

kernel/nsproxy.cより抜粋


setns()では、setns()をコールしたプロセスがCAP_SYS_ADMINを持っていることをチェックし switch_task_namespaces()でNamespaceを切り替えるという処理を行っています。

switch_task_namespaces()ではtask_struct構造体のnsproxyが指す構造体を切り替えています。

kernel/nsproxy.cより抜粋


この箇所よりPID 1のinitが持っているnsproxy構造体の情報をコピーして使うことで
Namespaceを親Namespaceに切り替えられそうなことがわかりました。
Cgroupとfile systemの情報も同じ方法でinitからコピーして使うにことにしましょう。

以下は、実際にLKMとしてkernel space上で実行するコード(jailbreak3.c)の中心部分です。

jailbreak3.c

プログラム末尾にはdropされたcapabilityをCAP_FULL_SETに設定する処理も追加しましたが、
これはContainer内では一部のcapabilityがdropされてしまっているからです。

注意:このプログラムはrace conditionに関して全く考慮していなので、運が悪いとOopsまたはKernel Panicなどでシステムが停止する可能性があります。


-Containerからの脱出-

jailbreak3.cをLKM(jailbreak3.ko)としてコンパイルします。jailbreak3.koをmylxcのrootfsにコピーした後にmylxc内でbashを起動します。


bashからkshを起動し。insmodでjailbreak3.koをloadします。
単純にinsmodするとkshがすぐに終了してしまう現象が発生するため、直後にkshからbashを起動します。
load前後でmount, ls, psの実行結果が全く異なることがわかります。

jailbreak3.koをloadする前

jailbreak3.koをloadした後

さらに、このkshから起動したbashはmylxcのCgroupではなくinitと同じcgroupに属しており、すべてのcapabilityを持っていることもわかります。

bashプロセスが所属するcgroup

bashプロセスのcapability

以上からkshから起動されたbashはContainerの外で動作していることがわかります。
上手くいったようです。

以上を踏まえてmylxc.confを修正します。mylxc内でのLKMのload & unloadを制限するとともに、networkの設定変更も制限します。
ここではinit_module()とdelete_module()をコールするためのcapabilityであるCAP_SYS_MODULEと、network interfaceの設定変更などを行うためのcapabilityであるCAP_NET_ADMINをdropする設定を追加しています。



-まとめ-

今回はnetworkとLKMの観点からLXCのセキュリティを検証しました。

これまでLXCのセキュリティを検証してきましたが、Container内でroot権限を与えた場合、
他の方法でもHost環境に対して攻撃が可能であることが知られています。[1]

前回と今回のコラムでセキュリティを考慮してLXCの設定を幾つか追加しましたが、
これでも十分に安全とは言えません。
Container内で稼働するroot権限のサービスを公開する場合は、
公開するサービスの種類に応じて、さらに権限を制限したほうが安全と思われます。

AppArmorやSELinuxなどのMAC(Mandatory Access Control)を併用することも考えられます。

また、DoSの可能性も考慮すべきと思われます。例えばLXCにはquotaの機能がありません。
Container内のプログラムがHost環境のディスク容量を消費し尽くしてしまわないように
適切にパーティションを分けることやfile systemの機能でquotaを設定することが必要です。

メモリやCPU等のリソースもCgroupの機能で制限することができます。


参考情報:
[1] EVADING FROM LINUX CONTAINERS
http://blog.bofh.it/debian/id_413
[2] lxc.conf
http://manpages.debian.net/cgi-bin/man.cgi?query=lxc.conf
[3] capabilities
http://manpages.debian.net/cgi-bin/man.cgi?query=capabilities
[4] BridgeNetworkConnections - Debian Wiki
https://wiki.debian.org/BridgeNetworkConnections
[5] LXC/SimpleBridge - Debian Wiki
https://wiki.debian.org/LXC/SimpleBridge


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