少し前に、コマンドライン引数としてcurl
に与えられたユーザー名とパスワードがps
出力に表示されないことに気付きました(もちろん、bashの履歴には表示される可能性があります)。
同様に、/proc/PID/cmdline
にも表示されません。
(ただし、組み合わせたユーザー名/パスワード引数の長さは導き出すことができます。)
以下のデモ:
[root@localhost ~]# nc -l 80 &
[1] 3342
[root@localhost ~]# curl -u iamsam:samiam localhost &
[2] 3343
[root@localhost ~]# GET / HTTP/1.1
Authorization: Basic aWFtc2FtOnNhbWlhbQ==
User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.15.3 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Host: localhost
Accept: */*
[1]+ Stopped nc -l 80
[root@localhost ~]# jobs
[1]+ Stopped nc -l 80
[2]- Running curl -u iamsam:samiam localhost &
[root@localhost ~]# ps -ef | grep curl
root 3343 3258 0 22:37 pts/1 00:00:00 curl -u localhost
root 3347 3258 0 22:38 pts/1 00:00:00 grep curl
[root@localhost ~]# od -xa /proc/3343/cmdline
0000000 7563 6c72 2d00 0075 2020 2020 2020 2020
c u r l nul - u nul sp sp sp sp sp sp sp sp
0000020 2020 2020 0020 6f6c 6163 686c 736f 0074
sp sp sp sp sp nul l o c a l h o s t nul
0000040
[root@localhost ~]#
この効果はどのようにして達成されますか?curl
のソースコードのどこかにありますか? (私はそれがcurl
機能ではなくps
機能であると思いますか?それともある種のカーネル機能ですか?)
また:これはバイナリ実行可能ファイルのソースコードの外部から実行できますか?例:シェルコマンドを使用して、おそらくルート権限と組み合わせて?
言い換えると、/proc
またはps
の出力(同じことだと思います)に渡された引数arbitraryシェルコマンド? (これに対する答えは「いいえ」だと思いますが、この余分な質問を含めることは価値があるようです。)
カーネルがプロセスを実行すると、コマンドライン引数がそのプロセスに属する読み取り/書き込みメモリにコピーされます(少なくともLinuxではスタック上)。プロセスは、他のメモリと同様にそのメモリに書き込むことができます。 ps
が引数を表示すると、プロセスのメモリの特定のアドレスに格納されているものをすべて読み取ります。ほとんどのプログラムは元の引数を保持しますが、それらを変更することは可能です。 ps
のPOSIX記述は、
表示された文字列が、開始時にコマンドに渡されたときの引数リストのバージョンであるか、アプリケーションによって変更された可能性があるため、引数のバージョンであるかは指定されていません。アプリケーションは、引数リストを変更でき、その変更をpsの出力に反映させることに依存できません。
これが言及されている理由は、ほとんどのunixバリアントは変更を反映していますが、他のタイプのオペレーティングシステムでのPOSIX実装は反映していない可能性があるためです。
プロセスは任意の変更を行うことができないため、この機能の使用は制限されています。少なくとも、プログラムはps
が引数をフェッチする場所を変更できず、元のサイズを超えて領域を拡張できないため、引数の全長を増やすことはできません。引数はCスタイルのnullで終了する文字列であるため、最後にnullバイトを置くことで長さを効果的に減らすことができます(これは最後に空の引数の束を持つことと区別できません)。
本当に掘り下げたい場合は、オープンソース実装のソースを見ることができます。 Linuxでは、ps
のソースは興味深いものではありません。/proc/PID/cmdline
の- proc filesystem からコマンドライン引数を読み取るだけです。このファイルのコンテンツを生成するコードは、カーネルの proc_pid_cmdline_read
in fs/proc/base.c
にあります。プロセスのメモリの一部(access_remote_vm
でアクセスされる)は、アドレスmm->arg_start
からmm->arg_end
に移動します。これらのアドレスは、プロセスの開始時にカーネルに記録され、後で変更することはできません。
一部のデーモンは、この機能を使用してステータスを反映します。彼らはargv[1]
をstarting
やavailable
やexiting
のような文字列に変更します。多くのUNIXバリアントには、これを行う setproctitle
関数があります。一部のプログラムは、この機能を使用して機密データを隠します。プロセスの開始中にコマンドライン引数が表示されるため、これは限定的に使用されることに注意してください。
ほとんどの高水準言語は、引数を文字列オブジェクトにコピーし、元のストレージを変更する方法を提供しません。以下は、argv
要素を直接変更することでこの機能を示すCプログラムです。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
int i;
system("ps -p $PPID -o args=");
for (i = 0; i < argc; i++)
{
memset(argv[i], '0' + (i % 10), strlen(argv[i]));
}
system("ps -p $PPID -o args=");
return 0;
}
出力例:
./a.out hello world
0000000 11111 22222
Curlソースコードでargv
の変更を確認できます。 Curlは function cleanarg
in src/tool_paramhlp.c
を定義します。これは、memset
を使用して引数をすべてのスペースに変更するために使用されます。 src/tool_getparam.c
では、この関数は数回使用されます。 ユーザーパスワード を編集する。この関数はパラメーター解析から呼び出されるため、curl呼び出しの早い段階で発生しますが、これが発生する前にコマンドラインをダンプすると、パスワードが表示されます。
引数はプロセス自身のメモリに保存されるため、デバッガを使用しない限り、外部から変更することはできません。
他の答えは一般的な方法で質問によく答えます。具体的には「」と答えるために、この効果はどのように達成されますか?それはどこかにありますかcurlのソースコードで? ":
curlソースコードの引数解析セクション では、_-u
_オプションは次のように処理されます。
_ case 'u':
/* user:password */
GetStr(&config->userpwd, nextarg);
cleanarg(nextarg);
break;
_
そして cleanarg()
関数が定義されています 以下のように:
_void cleanarg(char *str)
{
#ifdef HAVE_WRITABLE_ARGV
/* now that GetStr has copied the contents of nextarg, wipe the next
* argument out so that the username:password isn't displayed in the
* system process list */
if(str) {
size_t len = strlen(str);
memset(str, ' ', len);
}
#else
(void)str;
#endif
}
_
したがって、他の回答で説明されているように、argv
のusername:password引数がスペースで上書きされていることが明示的にわかります。
プロセスは、パラメータを読み取るだけでなく、書き込むこともできます。
私は開発者ではないので、私はこのことに詳しくありませんが、環境パラメータの変更と同様のアプローチで外部から可能である可能性があります: