$ k=v p &
[1] 3028
の実行中に言及しないp
ように内容を変更する方法はありますか?/proc/3028/environ
k=v
p
$ k=v p &
[1] 3028
の実行中に言及しないp
ように内容を変更する方法はありますか?/proc/3028/environ
k=v
p
回答:
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
環境変数を変更していない場合は、スタック上の文字列のアドレスを取得するために、次のようなこともできるはずです(そのうちの1つ)。setenv()
putenv()
k=v
#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つだけですが、に渡されるenvリストで両方k=v1
とk=v2
(またはk=v
2回)を渡すことを妨げるものはありませんexecve()
。これが、CVE-2016-2381などの過去のセキュリティ脆弱性の原因でした。bash
同じ名前で変数と関数の両方をエクスポートする場合、シェルショックの前に本当に発生する可能性があります。
いずれの場合でも、env var文字列がまだオーバーライドされていない間は常に小さなウィンドウが存在するため、次の方法で公開する場合は、秘密情報をコマンド(パイプなど)に渡す別の方法を見つけることができます。/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を参照してください。
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
です。
したがって、新しい引数または環境文字列(ベクターではなく、注意—指すメモリは実際の文字列データであり、連結および␀
区切られている必要があります)を作成し、それがどこにあるかをカーネルに通知するだけです。
これは、prctl(2)
関数のLinuxマニュアルページとマニュアルページに記載されていenviron(7)
ます。何されていない文書は、カーネルが終了アドレス、または開始アドレス以下の終了アドレス上の先頭アドレスを設定しようとする試みを拒否していることです。または、どちらかのアドレスをゼロに(再)設定します。また、これは、ブライアンドンランが2009年に提案した、単一の操作で開始と終了をアトミックに設定することを許可した元のメカニズムではありません。さらに、カーネルはこれらのポインターの現在の値を取得する方法を提供しません。
このため、を使用して環境とコマンドライン領域を変更するのが難しいprctl()
です。prctl()
最初と最後のデータがメモリ内のどこにあるかに応じて、最初の試行が開始ポインタを終了ポインタよりも高く設定しようとする可能性があるため、関数を4回まで呼び出す必要があります。これにより、システムの他のプロセスがプロセスのメモリ領域の任意の範囲を、新しい開始/終了の期間に検査する機会を与えないようにする場合は、さらに 4回呼び出す必要があります。設定されていますが、新しい終了/開始は設定されていません。
範囲全体を一度に設定する単一のアトミックシステムコールは、アプリケーションプログラムが安全に使用するのにはるかに簡単でした。
さらにシワが無い本当に良い理由(カーネルにチェックを与え、元のデータ領域のオーバーライトのために、つまり、とにかく Linux上で、これはスーパーユーザが必要で、かつ同等物は、BSD系のいずれかでの特権操作ではないという事実)特権。
私はこれを採用する私のツールセット用にかなりシンプルsetprocargv()
でsetprocenvv()
機能的なものを書きました。setenv
やなどの組み込みのツールセットからプログラムをチェーンロードするとforeground
、Linuxで許可されているコマンドの引数と環境のチェーンが反映されます。
#/ package / admin / nosh / command / clearenv setenv WIBBLE wobble foreground pause \; 本当& [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 |一時停止| 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つの疑似ファイルを介してではなく)直接メモリにアクセスするものに対しては影響を与えません。もちろん、この情報が表示される場所で文字列が変更される前にウィンドウを残します。メインスレッドのスタック上のデータを上書きするのと同じです。データを上書きする場合と同様に、これはさまざまな状況で(ヒープ上に)環境のコピーを作成する言語ランタイムライブラリを考慮していません。一般的に、これは、プログラムに「シークレット」を渡すための優れたメカニズムであるとは考えないでください。たとえば、名前のないパイプの読み取り側にオープンファイル記述子を継承し、完全に制御下の入力バッファーに読み取ります。その後、拭きます。
/proc/$pid/stat
(他の値のほかにあなたが必要な場合がありますstruct prctl_mm_map
)。小さなデモについては、filter_env.cの例も参照してください。JdeBP、setprocargv()
/ setprocenvv()
関数へのリンクを追加できますか?