日記 20180205 NetBSD Security Advisory 2018-002 について


NetBSD Security Advisory 2018-002 を今更知り,その内容がシェルスクリプトに関するものでしかも見落としがちなミスだったので覚えるためにまとめておく.

virecover (8) はvi (1)のリカバリファイルを作ったユーザに対して,それをメールで送り,実際のリカバリファイルは削除するシェルスクリプトだ.

NAME virecover -- report recovered vi edit sessions

SYNOPSIS /usr/libexec/virecover

DESCRIPTION The virecover utility sends emails to users who have vi(1) recovery files.

  This email gives the name of the file that was saved for recovery and
  instructions for recovering most, if not all, of the changes to the file.
  This is done by using the -r option with vi(1).  See the -r option in
  vi(1) for details.

  If the backup files have the execute bit set or are zero length, then
  they have not been modified, so virecover deletes them to clean up.
  virecover also removes recovery files that are corrupted, zero length, or
  do not have a corresponding backup file.

  virecover is normally run automatically at boot time using
  /etc/rc.d/virecover.

-- Jeremy C. Reed, 2006, VIRECOVER(8), System Manager's Manual


問題の,ファイルの削除に関する実装は以下のようになっている.

for i in $vibackup; do
    ...略..
    # Unmodified nvi editor backup files either have the 
    # execute bit set or are zero length.  Delete them.
    if test -x $i -o ! -s $i; then
        rm $i
    fi
done

変数iに格納されたファイルを検査し,実行可能ファイルであるか,0バイトの大きさのファイルであった場合には rm (1)でそれを削除する処理をおこなっている.

ここで,ファイル名に空白を含む「vi. /netbsd」というバックアップファイルを考える.「.(ドット)」の後にスペースがひとつ挟まっている.

このスクリプトで, $i に「vi. /netbsd」が格納されているとすると, rm (1)コマンドの引数は以下のように解釈される.

rm vi. /netbsd

つまり,カレントディレクトリの「vi.」と,「/netbsd」が rm(1) の引数となる.この例ではNetBSDカーネルが削除対象となってしまう.

対策は,変数展開のさいにダブルクォートを使う.修正版の /usr/libexec/virecover もそうなっている.

    rm "$i"

この場合,rm (1)コマンドの引数は以下のように解釈される.

rm "vi. /netbsd"

これはカレントディレクトリの「vi. /netbsd」を対象とした削除処理なので,NetBSDカーネルは削除されない.


簡単に実験できたし,変数展開のさいにはなるべくダブルクォートでくくらないといけないという良い例だと思った.