現在地

緊急コラム: 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 社のサポート対象ですが、十分な情報を得られない可能性が高いです。

  1. まず、 auditd サービスが稼働しているかどうかを確認し、稼働していない場合には稼働させます。
    [root@localhost ~]# service auditd status
    auditd is stopped
    [root@localhost ~]# service auditd start
    Starting auditd:                                           [  OK  ]
    
  2. /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
    
  3. この状態で、各種サービスなどを稼働させます。
  4. /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) のほうが簡単です。

以下に、 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 の概要および導入手順については、以下のページをご確認ください。

(*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 などについては、今後本コラムで取り上げますので、どうぞお楽しみに。