現在地
緊急コラム: bash 脆弱性( CVE-2014-6271 )の影響範囲の調査方法について
先週公開された bash の脆弱性について、管理されているサーバーへの影響を気にされている方が多いと思います。そこで緊急コラムとして、影響範囲の調査方法について紹介します。
bash は非常に多くの場面で使用されているプログラムであるため、 bash を呼び出す可能性のあるプログラムや場面をソースコード解析により列挙することは困難です。また、設定ファイルでの指定内容などにも依存する可能性もあり、文章でのお問合せにより漏れの無い的確な判断ができるかどうか不安が残ります。
Red Hat Enterprise Linux ( RHEL )には SELinux というセキュリティを強化するための機能が搭載されています。しかし、脆弱性の問題に限らず、システムのトラブル予防と適切な対処を行う上では、対象となるシステムの設定や挙動を事前に把握しておくことが重要であると考えます。
今回の脆弱性の影響範囲を調査する、および、今後類似の脆弱性が発見された場合に備えてシステムの設定や挙動を事前に把握しておくことができるように、実測に基づく調査方法の例を4通り紹介します。この連載は RHEL の利用者を対象にしていますが、十分な情報を得るためには Red Hat 社のサポート対象外となる機能 (*1) を併用する必要があることを予めご了承ください。
(*1) この連載の素材となっている、以下の資料で説明されている方法です。
エンタープライズ向けサーバのトラブル対応のための情報取得方法について
http://I-love.SAKURA.ne.jp/tomoyo/LCJ2014-ja.pdf
調査方法1: System Call Auditing だけを用いた調査
この方法は、 Red Hat 社のサポート対象ですが、十分な情報を得られない可能性が高いです。
- まず、 auditd サービスが稼働しているかどうかを確認し、稼働していない場合には稼働させます。
[root@localhost ~]# service auditd status auditd is stopped [root@localhost ~]# service auditd start Starting auditd: [ OK ]
- /bin/sh および /bin/bash の実行要求が /var/log/audit/audit.log に蓄積されるように設定します。
[root@localhost ~]# auditctl -a exit,always -F arch=b64 -S execve -F path=/bin/bash -k bash [root@localhost ~]# auditctl -a exit,always -F arch=b32 -S execve -F path=/bin/bash -k bash [root@localhost ~]# auditctl -a exit,always -F arch=b64 -S execve -F path=/bin/sh -k bash [root@localhost ~]# auditctl -a exit,always -F arch=b32 -S execve -F path=/bin/sh -k bash
- この状態で、各種サービスなどを稼働させます。
- /var/log/audit/audit.log に蓄積された実行要求を確認します。
以下の例は、 /usr/sbin/crond から /bin/sh -c "/usr/lib64/sa/sa1 1 1" という コマンドラインが実行され、 /bin/sh が /usr/lib64/sa/sa1 1 1 というコマンドラインを実行しようとしたところ、 /usr/lib64/sa/sa1 が #!/bin/sh で始まっているために、 /bin/sh /usr/lib64/sa/sa1 1 1 という形で sa1 が実行されたことを示しています。しかし、この情報だけでは crond から実行されていることを判断するのは困難です。---------- 実行結果例 ここから ---------- [root@localhost ~]# ausearch -k bash ---- time->Tue Sep 30 14:10:01 2014 type=PATH msg=audit(1412053801.710:149): item=1 name=(null) inode=2381259 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL type=PATH msg=audit(1412053801.710:149): item=0 name="/bin/sh" inode=1060864 dev=08:01 mode=0120777 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL type=CWD msg=audit(1412053801.710:149): cwd="/root" type=EXECVE msg=audit(1412053801.710:149): argc=3 a0="/bin/sh" a1="-c" a2=2F7573722F6C696236342F73612F73613120312031 type=SYSCALL msg=audit(1412053801.710:149): arch=c000003e syscall=59 success=yes exit=0 a0=7f2f79fb19b6 a1=7fffb4b44830 a2=7f2f79fb1b30 a3=8 items=2 ppid=2528 pid=2529 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=13 comm="sh" exe="/bin/bash" key="bash" ---- time->Tue Sep 30 14:10:01 2014 type=PATH msg=audit(1412053801.711:150): item=2 name=(null) inode=2381259 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL type=PATH msg=audit(1412053801.711:150): item=1 name=(null) inode=1060782 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL type=PATH msg=audit(1412053801.711:150): item=0 name="/usr/lib64/sa/sa1" inode=1748991 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL type=CWD msg=audit(1412053801.711:150): cwd="/root" type=EXECVE msg=audit(1412053801.711:150): argc=3 a0="/bin/sh" a1="/usr/lib64/sa/sa1" a2="1" type=EXECVE msg=audit(1412053801.711:150): argc=4 a0="/bin/sh" a1="/usr/lib64/sa/sa1" a2="1" a3="1" type=SYSCALL msg=audit(1412053801.711:150): arch=c000003e syscall=59 success=yes exit=0 a0=12c4900 a1=12c4ba0 a2=12c3af0 a3=20 items=3 ppid=2528 pid=2529 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=13 comm="sa1" exe="/bin/bash" key="bash" ---------- 実行結果例 ここまで ----------
調査方法2: System Call Auditing と独自カーネルモジュールを併用した調査
この方法は、 Red Hat 社のサポート対象外ですが、適切な量の情報を得られる可能性があります。
TaskTracker (*2) という独自カーネルモジュールを追加することにより、調査方法1で示したログに「どのアプリケーションから実行されているのか」を判断する手助けとなる情報が付加されるようになります。
ssh ログインした際のログインシェルである /bin/bash から /sbin/rebootコマンドを実行した場合のログを以下に示します。
---------- ログの例 ここから ---------- type=SYSCALL msg=audit(1397568834.719:20): arch=40000003 syscall=11 success=yes exit=0 a0=881e4e8 a1=882a500 a2=8820248 a3=882a500 items=2 ppid=1151 pid=1168 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="reboot" exe="/sbin/reboot" subj="tt-init(2014/04/15-22:32:04)=>init(2014/04/15-22:32:04)=> sh(2014/04/15-13:32:06)=>rc(2014/04/15-13:32:06)=> S55sshd(2014/04/15-13:32:20)=>sshd(2014/04/15-13:32:20)=> sshd(2014/04/15-13:32:33)=>bash(2014/04/15-13:32:37)=> reboot(2014/04/15-13:33:54)" key=(null) ---------- ログの例 ここまで ----------
(*2) 以下の場所からダウンロードできます。
調査方法3:独自カーネルモジュール AKARI を用いた調査
システムの挙動をプログラムの視点から追跡したり制限したりするためのカーネルの機能として TOMOYO Linux (*3) という機能があります。
TOMOYO Linux を導入すると、どのプログラムがどの資源にアクセスしているかをシステムコールレベルで把握することができるようになります。 (*4)
もし TOMOYO Linux を導入していたのであれば、今回のようなOSコマンド
インジェクション攻撃に対する防御策となります。 (*5)
なお、アクセス制限を行うことよりもアクセス解析を行うことが目的の場合、TOMOYO Linux の機能の一部をローダブルカーネルモジュールとして切り出したAKARI (*6) のほうが簡単です。
- (*3) TOMOYO Linux
http://tomoyo.sourceforge.jp/ - (*4) 以下に、 CentOS 5 ( RHEL 5 に相当するディストリビューション)に TOMOYO Linux を導入するデモ動画があります。
http://www.youtube.com/watch?v=kvmWLRddzaM - (*5) [tomoyo-users 984] TOMOYO/AKARI/CaitSith が bash 脆弱性に対してできること
http://sourceforge.jp/projects/tomoyo/lists/archive/users/2014-September/000998.html - (*6) AKARI
http://akari.sourceforge.jp/
以下に、 CentOS 6 に TOMOYO Linux を導入して実測した場合の結果の例を示します。
---------- history example start ---------- 0: <kernel> 1: /sbin/init 2: /bin/sh 3: /bin/awk 4: /bin/cat 5: /bin/grep 6: /bin/plymouth 7: /etc/rc.d/rc 8: /bin/plymouth 9: /etc/rc.d/init.d/auditd 10: /bin/bash 11: /sbin/auditd 12: /sbin/audispd 13: /bin/touch 14: /sbin/auditctl 15: /etc/rc.d/init.d/blk-availability 16: /bin/touch 17: /etc/rc.d/init.d/crond 18: /bin/bash 19: /usr/sbin/crond 20: /bin/touch 21: /etc/rc.d/init.d/ip6tables 22: /bin/awk 23: /bin/cat 24: /bin/grep 25: /bin/touch 26: /sbin/ip6tables-restore 27: /sbin/modprobe 28: /sbin/lsmod 29: /sbin/modprobe 30: /etc/rc.d/init.d/iptables 31: /bin/awk 32: /bin/cat 33: /bin/grep 34: /bin/touch 35: /sbin/iptables-restore 36: /sbin/modprobe 37: /sbin/lsmod 38: /sbin/modprobe 39: /etc/rc.d/init.d/iscsi 40: /bin/grep 41: /etc/rc.d/init.d/iscsid 42: /bin/awk 43: /bin/grep 44: /sbin/pidof 45: /etc/rc.d/init.d/lvm2-monitor 46: /bin/touch 47: /sbin/vgs 48: /etc/rc.d/init.d/mdmonitor 49: /usr/bin/id 50: /etc/rc.d/init.d/netfs 51: /bin/awk 52: /bin/mount 53: /bin/touch 54: /etc/rc.d/init.d/network 55: /bin/egrep 56: /bin/fgrep 57: /bin/ls 58: /bin/sed 59: /bin/sort 60: /bin/touch 61: /etc/sysconfig/network-scripts/ifup 62: /bin/awk 63: /bin/sed 64: /etc/sysconfig/network-scripts/ifup-eth 65: /bin/awk 66: /bin/cat 67: /bin/grep 68: /bin/ipcalc 69: /bin/sed 70: /etc/sysconfig/network-scripts/ifup-ipv6 71: /bin/awk 72: /bin/sed 73: /etc/sysconfig/network-scripts/ifup-post 74: /bin/awk 75: /bin/hostname 76: /bin/ipcalc 77: /bin/sed 78: /etc/sysconfig/network-scripts/ifup-aliases 79: /bin/awk 80: /sbin/ip 81: /etc/sysconfig/network-scripts/ifup-routes 82: /sbin/ip 83: /sbin/dhclient 84: /sbin/dhclient-script 85: /bin/awk 86: /bin/cat 87: /bin/cut 88: /bin/grep 89: /bin/ipcalc 90: /bin/mktemp 91: /bin/rm 92: /sbin/arping 93: /sbin/consoletype 94: /sbin/ip 95: /sbin/restorecon 96: /usr/bin/logger 97: /sbin/ethtool 98: /sbin/ip 99: /etc/sysconfig/network-scripts/init.ipv6-global 100: /sbin/ip 101: /sbin/sysctl 102: /sbin/arp 103: /sbin/sysctl 104: /etc/rc.d/init.d/postfix 105: /bin/basename 106: /bin/touch 107: /usr/sbin/postconf 108: /usr/sbin/postfix 109: /usr/libexec/postfix/postfix-script 110: /bin/sed 111: /usr/libexec/postfix/master 112: /usr/libexec/postfix/pickup 113: /usr/libexec/postfix/qmgr 114: /usr/libexec/postfix/postfix-script 115: /bin/egrep 116: /bin/find 117: /bin/grep 118: /bin/ls 119: /bin/sed 120: /bin/sh 121: /bin/grep 122: /bin/sed 123: /bin/uname 124: /usr/sbin/postconf 125: /usr/bin/cmp 126: /usr/sbin/postconf 127: /usr/sbin/postsuper 128: /usr/sbin/postconf 129: /usr/sbin/postlog 130: /etc/rc.d/init.d/rsyslog 131: /bin/bash 132: /sbin/rsyslogd 133: /bin/touch 134: /etc/rc.d/init.d/sshd 135: /bin/cat 136: /bin/touch 137: /sbin/runlevel 138: /usr/sbin/sshd 139: /usr/sbin/sshd 140: /bin/bash 141: /bin/grep 142: /bin/hostname 143: /sbin/consoletype 144: /usr/bin/dircolors 145: /usr/bin/id 146: /usr/bin/tput 147: /usr/bin/tty 148: /usr/sbin/ccs-editpolicy 149: /usr/sbin/ccs-savepolicy 150: /etc/rc.d/init.d/udev-post 151: /sbin/udevadm 152: /etc/rc.d/rc.local 153: /bin/touch 154: /sbin/consoletype 155: /sbin/initctl 156: /sbin/runlevel 157: /sbin/initctl 158: /sbin/mingetty 159: /bin/login 160: /bin/bash 161: /bin/grep 162: /bin/hostname 163: /sbin/consoletype 164: /usr/bin/dircolors 165: /usr/bin/id 166: /usr/bin/tput 167: /usr/bin/tty 168: /sbin/telinit 169: /etc/rc.d/rc.sysinit 170: /bin/awk 171: /bin/cat 172: /bin/chgrp 173: /bin/chmod 174: /bin/chown 175: /bin/dd 176: /bin/dmesg 177: /bin/find 178: /bin/rm 179: /bin/hostname 180: /bin/mkdir 181: /bin/mount 182: /sbin/mount.tmpfs 183: /bin/cut 184: /bin/grep 185: /bin/ls 186: /bin/mount 187: /bin/mv 188: /bin/plymouth 189: /bin/rm 190: /bin/sed 191: /bin/touch 192: /sbin/consoletype 193: /sbin/fsck 194: /sbin/fsck.ext4 195: /sbin/lvm 196: /sbin/modprobe 197: /sbin/pidof 198: /sbin/rmmod 199: /sbin/start_udev 200: /bin/awk 201: /bin/cat 202: /bin/chown 203: /bin/ln 204: /bin/mkdir 205: /bin/mknod 206: /sbin/consoletype 207: /sbin/fstab-decode 208: /bin/echo 209: /sbin/modprobe 210: /sbin/pidof 211: /sbin/restorecon 212: /sbin/rmmod 213: /sbin/udevadm 214: /sbin/udevd 215: /bin/bash 216: /etc/sysconfig/network-scripts/net.hotplug 217: /lib/udev/cdrom_id 218: /lib/udev/console_check 219: /lib/udev/console_init 220: /bin/loadkeys 221: /bin/sh 222: /bin/gzip 223: /bin/setfont 224: /bin/sh 225: /bin/gzip 226: /lib/udev/edd_id 227: /lib/udev/fstab_import 228: /lib/udev/path_id 229: /lib/udev/pci-db 230: /lib/udev/rename_device 231: /lib/udev/scsi_id 232: /lib/udev/write_net_rules 233: /sbin/blkid 234: /sbin/hwclock 235: /sbin/ifup 236: /bin/sed 237: /etc/sysconfig/network-scripts/ifup-eth 238: /bin/grep 239: /bin/ipcalc 240: /bin/sed 241: /etc/sysconfig/network-scripts/ifup-ipv6 242: /bin/sed 243: /sbin/consoletype 244: /etc/sysconfig/network-scripts/ifup-post 245: /bin/sed 246: /etc/sysconfig/network-scripts/ifup-aliases 247: /bin/awk 248: /sbin/consoletype 249: /sbin/ip 250: /etc/sysconfig/network-scripts/ifup-routes 251: /sbin/consoletype 252: /sbin/consoletype 253: /sbin/ip 254: /sbin/consoletype 255: /sbin/modprobe 256: /sbin/multipath 257: /sbin/swapon 258: /sbin/sysctl 259: /sbin/modprobe ---------- history example end ----------
インデントは「プログラムの実行を要求したプログラム/要求により実行されたプログラム」という関係を示します。つまり、 kernel が /sbin/init を実行し、kernel から実行された /sbin/init が /bin/sh を実行し、 kernel から実行された /sbin/init から実行された /bin/sh が /etc/rc.d/rc を実行するという具合です。(実際には、シェルスクリプトのインタプリタとして /bin/sh や /bin/bash が使われる場合があるため、上記の結果だけでなく ccs-domainmatch コマンドで検索した結果も併用します。)
このように /bin/sh や /bin/bash がどこで呼ばれているかを実測に基づき把握し、それぞれの呼び出し元について、悪意あるユーザーが細工された環境変数を渡すことができるかどうかを調査します。
調査方法4: SystemTap で作成したカーネルモジュールを用いた調査
この方法は、 SystemTap (*7) で作成したカーネルモジュール部分についてはRed Hat 社のサポート対象外ですが、適切な量の情報を得られる可能性があります。
TOMOYO Linux や AKARI を使う場合と比較すると、 SystemTap が提供する安全装置に起因する制約事項が増えてしまいますが、 (*8) に示すスクリプトを使用することで (*9) に示すような情報を手軽に取得することができます。
(*7) SystemTap の概要および導入手順については、以下のページをご確認ください。
- SystemTapとは何ですか? どのように使用しますか?
https://access.redhat.com/site/ja/articles/882873
(*8) 以下に示します。
---------- コマンドライン ここから ---------- # stap -g -DMAXSTRINGLEN=4096 -e ' global task_domain[32768]; function get_current:long() { return task_current() & %{ ULONG_MAX %}; } function is_success:long(ret:long) { return ret <= -4096 || ret >= 0; } function make_domain:string() { task = get_current(); if (task_domain[task] == "") task_domain[task] = sprintf("%s(%d)", execname(), pid()); return task_domain[task]; } probe kernel.function("copy_process").return { if (is_success($return)) task_domain[$return] = make_domain(); } probe kernel.function("do_execve") { make_domain(); } probe kernel.function("do_execve").return { if (is_success($return)) { task = get_current(); domain = task_domain[task]; if (domain != "") { filename = kernel_string($filename); printf("[%s] starting %s by uid=%d from %s\n", ctime(gettimeofday_s()), filename, uid(), domain); task_domain[task] .= " " . filename; } } } probe kernel.function("free_task") { delete task_domain[$tsk]; } probe end { delete task_domain; }' ---------- コマンドライン ここまで ----------
(*9) 以下に示します。
---------- 実行結果例 ここから ---------- [Sun May 4 00:54:16 2014] starting /bin/sh by uid=0 from init(1) [Sun May 4 00:54:16 2014] starting /sbin/mingetty by uid=0 from init(1) /bin/sh [Sun May 4 00:54:18 2014] starting /bin/login by uid=0 from init(1) /bin/sh /sbin/mingetty [Sun May 4 00:54:20 2014] starting /bin/bash by uid=0 from init(1) /bin/sh /sbin/mingetty /bin/login [Sun May 4 00:54:20 2014] starting /usr/bin/id by uid=0 from init(1) /bin/sh /sbin/mingetty /bin/login /bin/bash [Sun May 4 00:54:20 2014] starting /bin/hostname by uid=0 from init(1) /bin/sh /sbin/mingetty /bin/login /bin/bash [Sun May 4 00:54:20 2014] starting /usr/bin/tty by uid=0 from init(1) /bin/sh /sbin/mingetty /bin/login /bin/bash [Sun May 4 00:54:20 2014] starting /usr/bin/tput by uid=0 from init(1) /bin/sh /sbin/mingetty /bin/login /bin/bash [Sun May 4 00:54:20 2014] starting /usr/bin/dircolors by uid=0 from init(1) /bin/sh /sbin/mingetty /bin/login /bin/bash [Sun May 4 00:54:20 2014] starting /bin/grep by uid=0 from init(1) /bin/sh /sbin/mingetty /bin/login /bin/bash [Sun May 4 00:54:20 2014] starting /sbin/consoletype by uid=0 from init(1) /bin/sh /sbin/mingetty /bin/login /bin/bash [Sun May 4 00:54:35 2014] starting /usr/bin/ssh by uid=0 from init(1) /bin/sh /sbin/mingetty /bin/login /bin/bash ---------- 実行結果例 ここまで ----------
本記事は、bashの脆弱性の影響範囲の調査方法として作成しましたが、その内容は、bashあるいは脆弱性の確認に限定されるものではなく、管理されているLinuxサーバの動作内容を把握するものとなっています。調査方法は、Red Hat サポートの範囲内で利用できる方法とそうでないものがありますが、読者の方々の状況により使い分けて活用いただければと思います。なお、TOMOYO Linuxなどについては、今後本コラムで取り上げますので、どうぞお楽しみに。