web-dev-qa-db-ja.com

noexecオプションを使用してファイルシステムでbashスクリプトまたはcバイナリを実行する

誰もが以下で何が起こっているのか詳細に説明できますか?次のようにnoexecオプションを使用してディレクトリをマウントするとします。

mount -o noexec /dev/mapper/Fedora-data /data

これを確認するために、私はmount | grep dataを実行しました:

/dev/mapper/Fedora-data on /data type ext4 (rw,noexec,relatime,seclabel,data=ordered)

/data内で、次のようにhello_worldという簡単なスクリプトを作成しています。

#!/bin/bash

echo "Hello World"
whoami

そのため、スクリプトをchmod u+x hello_worldで実行可能にしました(ただし、これはnoexecオプションを使用したファイルシステムには影響しません)。実行しようとしました。

# ./hello_world
-bash: ./hello_world: Permission denied

ただし、bashをファイルに事前に追加すると、次のようになります。

# bash hello_world
Hello World
root

それで、次の内容のシンプルなhello_world.cを作成しました。

#include <stdio.h>

int main()
{
    printf("Hello World\n");
    return 0;
}

cc -o hello_world hello_world.cを使用してコンパイルしました

現在実行中:

# ./hello_world
-bash: ./hello_world: Permission denied

だから私はそれを使って実行しようとしました

/lib64/ld-linux-x86-64.so.2 hello_world

エラー:

./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted

したがって、lddは以下を返すため、これはもちろん当てはまります。

ldd hello_world
ldd: warning: you do not have execution permission for `./hello_world'
    not a dynamic executable

noexecマウントオプションが適用されない別のシステムでは、次のようになります。

ldd hello_world
    linux-vdso.so.1 (0x00007ffc1c127000)
    libc.so.6 => /lib64/libc.so.6 (0x00007facd9d5a000)
    /lib64/ld-linux-x86-64.so.2 (0x00007facd9f3e000)

今私の質問はこれです:noexecオプションを使用してファイルシステムでbashスクリプトを実行しても、cコンパイル済みプログラムが機能しないのはなぜですか?内部で何が起こっていますか?

10

どちらの場合も同じです。ファイルを直接実行するには、実行ビットを設定する必要があり、ファイルシステムをnoexecでマウントできません。しかし、これらはこれらのファイルの読み取りを停止するものではありません。

Bashスクリプトが./hello_worldとして実行され、ファイルが実行可能でない場合(exec許可ビットがない、またはファイルシステムでnoexecのいずれか)、#!システムはファイルもロードしないため、チェックされません。スクリプトは、関連する意味で「実行」されることはありません。

bash ./hello_worldの場合、まあ、単純なnoexecファイルシステムオプションは、思ったほどスマートではありません。実行されるbashコマンドは/bin/bashであり、/binnoexecのあるファイルシステム上にありません。したがって、問題なく実行されます。システムはbash(またはpythonまたはPerlなど)がインタプリタであることを気にしません。それは、あなたが与えたコマンド(/bin/bash)をたまたま引数として実行するだけですファイル。bashまたは別のシェルの場合、そのファイルには実行するコマンドのリストが含まれていますが、今はファイルの実行ビットをチェックするすべてのものを「貼り付け」ています。このチェックは、後で何が起こるかについては責任を負いません。

この場合を考えてみましょう:

$ cat hello_world | /bin/bash

…または猫の無意味な使用を好まない人のために:

$ /bin/bash < hello_world

ファイルの先頭にある "shbang" #!シーケンスは、ファイルをコマンドとして実行しようとするときに同じことを効果的に実行するための素晴らしい魔法です。このLWN.netの記事が役に立つかもしれません: プログラムの実行方法

26
mattdm

以前の回答は、インタープリター(あなたの場合は/bin/bash)がコマンドラインから明示的に呼び出されたときに、noexec設定がスクリプトの実行を妨げない理由を説明しています。しかし、それがすべてだった場合、このコマンドも機能します。

/lib64/ld-linux-x86-64.so.2 hello_world

そして、あなたが指摘したように、それは機能しません。これは、noexecにも別の効果があるためです。カーネルは、PROT_EXECが有効になっているファイルシステムからのメモリマップファイルを許可しません。

メモリマップファイルは、複数のシナリオで使用されます。最も一般的な2つのシナリオは、実行可能ファイルとライブラリのシナリオです。 execveシステムコールを使用してプログラムを起動すると、カーネルは内部でリンカーと実行可能ファイルのメモリマッピングを作成します。その他の必要なライブラリは、PROT_EXECを有効にしてmmapシステムコールを介してリンカーによってマップされたメモリです。 noexecを使用してファイルシステムからライブラリを使用しようとすると、カーネルはmmap呼び出しを拒否します。

/lib64/ld-linux-x86-64.so.2 hello_worldを呼び出すと、execveシステムコールはリンカーのメモリマッピングのみを作成し、リンカーはhello_world実行可能ファイルを開いて、ほぼ同じようにメモリマッピングを作成しようとします。それが図書館のためになしたであろう方法。そして、これがカーネルがmmap呼び出しの実行を拒否し、エラーが発生するポイントです。

./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted

noexec設定は、実行許可なしでメモリマッピングを許可し(データファイルに使用される場合があります)、ファイルの通常の読み取りも許可するため、bash hello_worldが役立ちました。

11
kasperd

この方法でコマンドを実行する:

bash hello_world

ファイルからbashを読み取らせるhello_world(禁止されていません)。

その他の場合、OSはこのファイルを実行しようとしますhello_worldnoexecフラグのために失敗

3
Romeo Ninov

Bashを介してスクリプトを実行すると、ファイルを読み込んで解釈するだけです。

ただし、名前をカーネルに渡すと、ファイルの「#!」が本当に検査されます。そして、カーネルオプション「CONFIG_BINFMT_SCRIPT」に従って指定されたインタプリタをロードします。それは言う:

「#!」で始まる解釈済みスクリプトを実行する場合は、ここでYと言います。その後にインタープリターへのパスが続きます。

このサポートはモジュールとして構築できます。ただし、そのモジュールが読み込まれるまで、スクリプトを実行できません。したがって、このモジュールをinitramfsからロードする場合、このモジュールをロードする前のinitramfsの部分は、コンパイルされたバイナリのみで構成されている必要があります。

ここでMまたはNと言った場合、ほとんどのシステムは起動しません。不明な場合はYと言ってください。

上記は、このオプションに関連するヘルプテキストです。別の興味深い違いについて。私は自分のスクリプトを書いた:

> cat myprog.sh
#!/bin/cat

echo "Hello World"
> chmod +x myprog.sh

Bashで実行すると、bashのインタープリターが実行されます。

> bash myprog.sh
Hello World

ただし、カーネルは次のことを行います。

> myprog.sh
#!/bin/cat

echo "Hello World"

カーネルは 'cat'を呼び出したため、1行目を含むスクリプトを出力しました。

Cプログラムの場合、バイナリーを実行するためにインタープリターを呼び出すことはありません。カーネルはそれを直接実行しようとします。それでも、一部のデバッガーを使用してすべての実行可能ファイルをメモリにロードした場合でも、デバッガーを介してロードされているため、プログラムを「実行」できます。

「noexec」オプションは、バイナリの実行ビットをオフにして、カーネルがバイナリを「自然に」実行することを無効にするようなものです。

ところで、プログラムにSetUIDビットが設定されている場合は違いがあります-インタープリターでプログラムをロードしてもUIDは設定されません。カーネルがロードしたときにのみ、その特権を有効にできます。

FWIW-Windowsにも同じタイプのメカニズムがあります。

「.exe」や「.vbs」などの「実行可能なサフィックス」として「.sh」を追加すると、実行する「.sh」ファイルの設定方法に従って、ウィンドウが自動的にファイルを実行します。理論的には、「。txt」ファイルを設定して、コマンドラインで名前を入力した場合に自動的に入力されるようにすることができます。

同様に、テキストファイルを画面に出力するプログラムに短い呼び出しをかけることができます。そのため、公共の場所にログインしたままにしないでください。

2
Astara

Bash実行可能ファイルが上記のファイルシステムに存在しないためです。

0
tink