GNU/Linux >> Linux の 問題 >  >> Linux

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

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=" 文字列が割り当てられます。

他に k を変更していない場合 setenv() の環境変数 /putenv() 、そして 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;
}

ただし、1 しか削除されないことに注意してください k=v の 環境で受信したエントリ。通常は 1 つしかありませんが、両方の k=v1 を通過することを妨げるものは何もありません および k=v2 (または k=v 2 回) execve() に渡される環境リストで .これは、CVE-2016-2381 などの過去のセキュリティ脆弱性の原因でした。 bash で実際に発生する可能性があります 変数と関数の両方を同じ名前でエクスポートするときのシェルショックの前。

いずれにせよ、env var 文字列がまだ上書きされていない小さなウィンドウが常に存在するため、secret を渡す別の方法を見つけることをお勧めします。 /proc/pid/environ 経由で公開する場合のコマンドへの情報 (たとえば、パイプなど) 懸念事項です。

また、/proc/pid/cmdline とは逆に注意してください。 、 /proc/pid/environment 同じ euid または root を持つプロセスからのみアクセスできます (または、プロセスの euid と ruid が同じでない場合は root のみ)。

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

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


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

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

したがって、新しい引数または環境文字列を作成する必要があるだけです (ベクトルではありません。注意してください — 指すメモリは、実際の文字列データを連結して する必要があります)。 -delimited) をカーネルに伝え、それがどこにあるかを伝えます。

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

これにより、変更が難しくなります prctl() を使用した環境およびコマンド ライン領域 . prctl() を呼び出す必要があります これは、古いデータと新しいデータがメモリ内のどこにあるかによって、最初の試行で開始ポインターが終了ポインターよりも高く設定される可能性があるためです。 さらにと呼ぶ必要があります これにより、システム上の他のプロセスが、新しい開始/終了が設定されているが新しい終了が設定されている期間に、プロセスのメモリ空間の任意の範囲を検査する機会のウィンドウにならないようにしたい場合は、4 回/start は行っていません。

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

もう 1 つの問題は、正当な理由がないことです (カーネルでのチェックを考えると、元のデータ領域の上書き可能性は とにかく 、および同等の操作がどの BSD でも特権操作ではないという事実)、Linux ではこれにはスーパーユーザー特権が必要です。

かなり単純な setprocargv() を書きました と setprocenvv() これを使用するツールセットの関数。 setenv など、組み込みのツールセットからプログラムをチェーンロードする と foreground 、したがって、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  |foreground.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                                 |pause.|
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 つの疑似ファイルではなく) でそのメモリに直接アクセスするものには影響しないことに注意してください。もちろん、文字列が変更される前に、この情報を確認できるウィンドウを残します。メインスレッドのスタックの上のデータを上書きするのと同じように。また、データを上書きする場合と同様に、さまざまな状況で (ヒープ上に) 環境のコピーを作成する言語ランタイム ライブラリは考慮されていません。一般に、これは、(たとえば) 開いているファイル記述子を名前のないパイプの読み取り側に継承させ、完全に制御下で入力バッファーに読み込むように、プログラムに「秘密」を渡すための優れたメカニズムであるとは考えないでください。

さらに読む

  • ティモ・シライネン (2009-10-02)。 prctl() に PR_SET_PROCTITLE_AREA オプションを追加 . Linux カーネル メーリング リスト。
  • https://unix.stackexchange.com/a/432681/5132
  • ダニエル J. バーンスタイン。 checkpassword インターフェース . cr.yp.to.
  • https://github.com/jdebp/nosh/blob/master/source/setprocargv.cpp
  • https://github.com/jdebp/nosh/blob/master/source/setprocenvv.cpp

Linux
  1. Unix / Linux でプロセスのパスを取得するにはどうすればよいですか

  2. Linux で /proc/pid/pagemap エントリをデコードするには?

  3. pid ファイルによるプロセスの強制終了

  1. /dev/shm/ と /tmp/ はいつ使用する必要がありますか?

  2. /proc/meminfo の MemTotal が変更されるのはなぜですか?

  3. 推奨される使用方法に従って、Web サイトは /var/ または /usr/ に配置する必要がありますか?

  1. Linuxは複数の連続したパスセパレーター(/ home //// username /// file)をどのように処理しますか?

  2. Linux –Linuxで/proc / $ pid / memから読み取る方法は?

  3. Linux – / proc/mntを/proc/ mountsにリンクしますか?