web-dev-qa-db-ja.com

プロセスの開始後に/ proc / PID / environを変更します

$ k=v p &
[1] 3028

p/proc/3028/environの内容を変更してk=vに触れないようにする方法はありますかwhilepはまだ実行中ですか?

10
Cetin Sert

Linuxでは、スタック上の環境文字列の値を上書きできます。

したがって、エントリをゼロまたはその他で上書きすることにより、エントリを非表示にすることができます。

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[], char* envp[]) {
  char cmd[100];

  while (*envp) {
    if (strncmp(*envp, "k=", 2) == 0)
      memset(*envp, 0, strlen(*envp));

    envp++;
  }

  sprintf(cmd, "cat /proc/%u/environ", getpid());

  system(cmd);
  return 0;
}

次として実行:

$ env -i a=foo k=v b=bar ./wipe-env | hd
00000000  61 3d 66 6f 6f 00 00 00  00 00 62 3d 62 61 72 00  |a=foo.....b=bar.|
00000010

k=v\0\0\0で上書きされました。

値を上書きするsetenv("k", "", 1)はその場合は機能しないことに注意してください。新しい"k="文字列が割り当てられます。

setenv()/putenv()を使用してk環境変数を変更していない場合は、次のようにしてk=v文字列のアドレスを取得することもできます。スタック上(まあ、そのうちの1つ):

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(int argc, char* argv[]) {
  char cmd[100];
  char *e = getenv("k");

  if (e) {
    e -= strlen("k=");
    memset(e, 0, strlen(e));
  }

  sprintf(cmd, "cat /proc/%u/environ", getpid());

  system(cmd);
  return 0;
}

ただし、環境で受信されたk=vエントリのoneのみが削除されることに注意してください。通常は1つだけですが、execve()に渡されるenvリストでk=v1k=v2(またはk=vの両方)を渡すことを妨げるものはありません。 CVE-2016-2381 など、過去のセキュリティの脆弱性の原因でした。同じ名前で変数と関数の両方をエクスポートするときに、Shellshockより前のbashで本当に発生する可能性があります。

いずれの場合でも、env var文字列がまだオーバーライドされていない小さなウィンドウが常にあるため、secret情報を/proc/pid/environを介してそれを公開することが懸念される場合は、コマンド(たとえば、パイプなど).

また、/proc/pid/cmdlineとは異なり、/proc/pid/environmentには同じeuidまたはroot(またはrootがプロセスのeuidとruidが異なる場合に限り、見た目が同じでない場合)のみがアクセスできることにも注意してください。

その値を/proc/pid/environで非表示にすることができますが、たとえばデバッガーをそれに接続することにより、メモリ内の文字列から作成した他のコピーを引き続き取得できる場合があります。

少なくともroot以外のユーザーがこれを実行できないようにする方法については、 https://www.kernel.org/doc/Documentation/security/Yama.txt を参照してください。

12

上記の文字列(実際にはonではない)をLinuxのメインスレッドのスタックに上書きする必要はありません2010年以来。

/proc/self/cmdline/proc/self/environの両方は、実行時にプロセス自体によって、それぞれPR_SET_MM_ARG_START + PR_SET_MM_ARG_ENDまたはPR_SET_MM_ENV_START + PR_SET_MM_ENV_ENDを使用してprctl()関数を呼び出すことにより、変更可能です。これらは、/proc/${PID}/cmdline/proc/${PID}/environの内容、およびpsによって報告されたコマンドラインと環境を取得するために使用される、プロセスごとのカーネルによって保持されるプロセスのアプリケーションメモリ空間にメモリポインターを直接設定しますコマンド。

したがって、新しい引数または環境文字列(ベクターではなく、注意してください。ポイントされるメモリは、実際の文字列データであり、連結され、- delimitedである必要があります)を作成し、それをカーネルに通知する必要があります。

これは、prctl(2)関数のLinuxマニュアルページとenviron(7)マニュアルページに記載されています。文書化されているではないとは、カーネルが開始アドレスを終了アドレスより上、または終了アドレスを開始アドレスより下に設定しようとする試みを拒否することです。または、どちらかのアドレスをゼロに(再)設定します。また、これは、ブライアンドンランが2009年に提案した、開始と終了を単一の操作でアトミックに設定できる元のメカニズムではありません。さらに、カーネルはこれらのポインターの現在の値をgetする方法を提供しません。

これにより、prctl()を使用して環境とコマンドライン領域をmodifyするのが難しくなります。 prctl()関数を4回まで呼び出す必要があります。これは、最初と最後のデータがメモリ内のどこにあるかによって、開始ポインタを終了ポインタより高く設定しようとする場合があるためです。これがシステムの他のプロセスが検査する機会の窓にならないことを確実にしたい場合、それをthertherと4回呼び出す必要があります新しい開始/終了が設定されたが、新しい終了/開始が設定されていない期間のプロセスのメモリ空間の任意の範囲。

範囲全体を一度に設定する単一のアトミックシステムコールは、アプリケーションプログラムが安全に使用するのにはるかに簡単でした。

さらなるしわは、本当に正当な理由がない(カーネルでのチェックが与えられた場合、元のデータ領域の上書き可能性とにかく)、および相当するものは、どのBSDでも特権操作ではありません)。Linuxでは、これにはスーパーユーザー特権が必要です。

これを使用するツールセット用のかなり単純なsetprocargv()関数とsetprocenvv()関数を作成しました。 setenvforegroundのような組み込みのツールセットからプログラムをチェーンロードすると、Linuxが許可する、チェーンされたコマンドの引数と環境が反映されます。

#/ package/admin/nosh/command/clearenv setenv WIBBLE wobble foreground pause \; true&
 [1] 1057 
#hexdump -C /proc/1057/cmdline
00000000 66 6f 72 65 67 72 6f 75 6e 64 00 70 61 75 73 65 |前景.pause | 
 00000010 00 3b 00 74 72 75 65 00 |。;。true。| 
 00000018 
#hexdump -C /proc/1057/environ
 00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = wobble。| 
 0000000e 
#hexdump -C /proc/1058/cmdline
00000000 70 61 75 73 65 00 |一時停止|| 
 00000006 
#hexdump -C /proc/1058/environ
00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = wobble。| 
 0000000e 
#

これは、プロセスをトレースし、他の方法で(これらの2つの疑似ファイルを介してではなく)直接メモリにアクセスするものに対して影響を与えず、この情報が表示される文字列が変更される前にウィンドウを残すことに注意してください。メインスレッドのスタック上のデータを上書きするのと同じです。また、データの上書きの場合と同様に、さまざまな状況で(ヒープ上に)環境のコピーを作成する言語ランタイムライブラリは考慮されません。一般に、これは、プログラムに「秘密」を渡すための優れたメカニズムであるとは見なさないでください。たとえば、名前のないパイプの読み取り側にオープンファイル記述子を継承し、完全に制御下の入力バッファーに読み取ります。その後、拭きます。

参考文献

8
JdeBP