過去の記事 で、find コマンドの -exec アクションについて調べたことがありましたが、マニュアルには -execdir アクションを使うほうが安全とあります。

少し気になったので調べてみました。

 

 

まずは、マニュアルをざっと見てみます。

 

find -execdir

以下、マニュアルの要約。

-exec と似ていますが、指定されたコマンドはマッチしたファイルのあるサブディレクトリで実行されます。

-exec と同じように、シェルから find を実行する場合は、{} を引用符で囲む必要があります。

このオプションは、マッチしたファイルのパス解析の競合を避けることで、コマンドを実行するのによりセキュアな方法になります。

-exec アクションと同様、-execdir+ 形式は、一つ以上のマッチしたファイルを処理するためにコマンドラインを組み立てますが、与えられたコマンドの起動は、同じサブディレクトリにあるファイルのみリストします。

もしこのオプションを使用する場合は、環境変数 $PATH. (カレントディレクトリ) を参照していないことを確実にしておく必要があります。そうでないと、-execdir を実行するディレクトリに適当に名付けられたファイルを置いておくことで、攻撃者は好みのコマンドを実行することが可能になります。これは、$PATH が空であったり、ディレクトリが絶対参照でない場合も同様です。

+ 形式での実行の終了ステートが 0 でない値であるとき、find は 0 でない終了ステートを返します。

find がエラーに遭遇した場合は、即座に終了する場合があり、その場合は残りのコマンドがまったく実行されないままになります。

アクションの結果は、+; のどちらが使用されているかによって左右されます。-execdir command {} + は常に true を返し、-execdir command {} ; は、コマンドの終了ステートが0の場合のみに true を返します。

 

find -execdir を使用してみる

次のようにしてサンプルになるディレクトリとファイルを作成して、-execdir の動作を確認してみます。

$ mkdir findtest
$ mkdir findtest/dir1
$ mkdir findtest/dir2/dir3
$ touch findtest/dir1/myfile1
$ touch findtest/dir2/myfile2
$ touch findtest/dir2/dir3/myfile3

 

tree コマンドでディレクトリツリーを確認すると以下のようになっています。

$ tree --charset C findtest/
findtest/
|-- dir1
|   `-- myfile1
`-- dir2
    |-- dir3
    |   `-- myfile3
    `-- myfile2

 

まず、基本形として myfile* にマッチするファイルを検索し、-print してみます。

$ cd findtest
$ find . -name 'myfile*' -print
./dir2/myfile2
./dir2/dir3/myfile3
./dir1/myfile1

 

次に -exec で実行します。

$ find . -name 'myfile*' -exec ls -l \;
total 8
drwxr-xr-x 2 zrd zrd 4096 Jan  7 16:07 dir1
drwxr-xr-x 3 zrd zrd 4096 Jan  7 16:18 dir2
total 8
drwxr-xr-x 2 zrd zrd 4096 Jan  7 16:07 dir1
drwxr-xr-x 3 zrd zrd 4096 Jan  7 16:18 dir2
total 8
drwxr-xr-x 2 zrd zrd 4096 Jan  7 16:07 dir1
drwxr-xr-x 3 zrd zrd 4096 Jan  7 16:18 dir2

 

-exec アクションは実行したディレクトリでコマンドを実行するので、findtest ディレクトリでのコマンドの実行結果が表示されています。

次に、-execdir アクションで実行してみます。

$ find . -name 'myfile*' -execdir ls -l \;
total 4
drwxr-xr-x 2 zrd zrd 4096 Jan  7 16:18 dir3
-rw-r--r-- 1 zrd zrd    0 Jan  7 16:07 myfile2
total 0
-rw-r--r-- 1 zrd zrd 0 Jan  7 16:18 myfile3
total 0
-rw-r--r-- 1 zrd zrd 0 Jan  7 16:07 myfile1

 

こちらは、マニュアルに書いてあるように、マッチしたファイルが有るディレクトリで実行されているのがわかりますね。

 

find -execdir もう一つの例を考える

先程の例を少し改造して、次のようなディレクトリ構造を作成して、以下のそれぞれのディレクトリにファイル myfile を作成してみたいとします。

# このようなディレクトリ構造
../findtest
|-- dir1
`-- dir2
    `-- dir3
        `-- dir4

#  このようにしたい
../findtest
|-- dir1
|   `-- myfile
`-- dir2
    |-- dir3
    |   |-- dir4
    |   |   `-- myfile
    |   `-- myfile
    `-- myfile

 

ディレクトリを検索する場合には、以下のように -type d とオプションを指定します。

この例の場合は、以下のように4つのディレクトリが発見されます。

$ find . -type d -print
.
./dir2
./dir2/dir3
./dir2/dir3/dir4
./dir1

 

このコマンドのアクションを -execdir に変更して、次のようにして実行してみます。

$ find . -type d -execdir touch myfile \;

 

実行後のディレクトリツリーは次のようになりました。

$ tree ../findtest --charset c
../findtest
|-- dir1
|-- dir2
|   |-- dir3
|   |   |-- dir4
|   |   `-- myfile
|   `-- myfile
`-- myfile

4 directories, 3 files

 

見てみると dir1dir4 でファイルが作成されていないのが解ります。

マニュアルにも書かれていますが、

the specified command is run from the subdirectory containing the matched file...(マッチしたファイル(ディレクトリ)のあるサブディレクトリで実行される)

とあります。

つまり、以下のように解釈刷ることができます。

cd . && touch myfile           # ./dir2 にマッチ(含まれているサブディレクトリは .)
cd ./dir2 && touch myfile      # ./dir2/dir3 にマッチ(含まれているサブディレクトリは ./dir2)
cd ./dir2/dir3 && touch myfile # ./dir2/dir3/dir4 にマッチ(含まれているサブディレクトリは ./dir2/dir3)
cd . && touch myfile           # ./dir1 にマッチ(含まれているサブディレクトリは .)

 

この場合、dir4 は、dir3 に含まれているので、dir3 ディレクトリで touch コマンドが実行されているのです。この辺りは意外に盲点かもしれません。

もし、期待どおりに動作させたい場合は、次のように -exec と組み合わせて実行する例があります。

find . -type d -exec sh -c 'touch "$0/myfile"' {} \;

 

環境変数 PATH に . が含まれている場合

環境変数 PATH. を含ませることで、カレントディレクトリにある実行可能なファイルを ./ なしに実行することができますが、このような設定はご法度とされています。

-execdir アクションを使用する場合、PATH にカレントディレクトリ . を含めるのはセキュリティ上問題があるとのことですが、私の環境 (Ubuntu 20.04 LTS) 上では、PATH にカレントディレクトリが含まれている場合、次のように警告が出ます。

$ find . -type d -execdir touch myfile \;
find: The current directory is included in the PATH environment variable, which is insecure in combination with the -execdir action of find.  Please remove the current directory from your $PATH (that is, remove ".", doubled colons, or leading or trailing colons)

 

 
Share this...
カテゴリー: Linuxubuntuコマンド

zaturendo

中小企業社内SE。

0件のコメント

コメントを残す

アバタープレースホルダー

メールアドレスが公開されることはありません。