web-dev-qa-db-ja.com

curlは、ps出力に表示されるパスワードをどのように保護しますか?

少し前に、コマンドライン引数として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シェルコマンド? (これに対する答えは「いいえ」だと思いますが、この余分な質問を含めることは価値があるようです。)

71
Wildcard

カーネルがプロセスを実行すると、コマンドライン引数がそのプロセスに属する読み取り/書き込みメモリにコピーされます(少なくとも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]startingavailableexitingのような文字列に変更します。多くの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引数がスペースで上書きされていることが明示的にわかります。

14
Digital Trauma

プロセスは、パラメータを読み取るだけでなく、書き込むこともできます。

私は開発者ではないので、私はこのことに詳しくありませんが、環境パラメータの変更と同様のアプローチで外部から可能である可能性があります:

https://stackoverflow.com/questions/205064/is-there-a-way-to-change-another-processs-environment-variables

3
Hauke Laging