コマンドライン引数として渡されたパスワードを非表示にする方法は?


43

私は特定のアクションがパスフレーズを入力するために必要なソフトウェアデーモンを実行しています。

$ darkcoind masternode start <mypassphrase>

今、私は私のヘッドレスdebianサーバーでいくつかのセキュリティ上の懸念を得ました。

たとえば、bashの履歴を検索すると、Ctrl+Rこの強力なパスワードが表示されます。サーバーが危険にさらされ、一部の侵入者がシェルアクセスを持ち、単純Ctrl+Rに履歴でパスフレーズを見つけることができると想像します。

それはbashの歴史の中で示した、ことをせずにパスフレーズを入力する方法はありますps/proc他のどこか?


更新1:デーモンにパスワードを渡さないとエラーがスローされます。これはオプションではありません。


更新2:ソフトウェアを削除したり、開発者をハングアップするなどのその他の役立つヒントを削除したりしないでください。私はこれがベストプラクティスの例ではありません知っているが、このソフトウェアは、に基づいてビットコインとすべてのビットコインベースのクライアントは、これらのコマンドに耳を傾け、その既知のセキュリティ上の問題はまだ(議論されてJSON RPCサーバのいくつかの種類あり、BC) 。


更新3:デーモンは既に開始され、コマンドで実行されています

$ darkcoind -daemon

実行するとps、起動コマンドのみが表示されます。

$ ps aux | grep darkcoin
user     12337  0.0  0.0  10916  1084 pts/4    S+   09:19   0:00 grep darkcoin
user     21626  0.6  0.3 1849716 130292 ?      SLl  May02   6:48 darkcoind -daemon

だから、パスフレーズでコマンドを渡すことに表示されないpsか、/procまったく。

$ darkcoind masternode start <mypassphrase>
$ ps aux | grep darkcoin
user     12929  0.0  0.0  10916  1088 pts/4    S+   09:23   0:00 grep darkcoin
user     21626  0.6  0.3 1849716 130292 ?      SLl  May02   6:49 darkcoind -daemon

これにより、履歴がどこに表示されるのかという疑問が残ります。だけで.bash_history


1
最初の質問は、パスフレーズ引数なしでデーモンを起動するとどうなるかということです。プロンプトが表示されるだけですか?
MadHatterは、

31
うまくいく答えはないと思います。パスフレーズを要求できないことは、デーモンの大きな欠点です。フリーソフトウェアの場合は、プログラマーを入れて修正します。変更を公開することを忘れないでください。プロプライエタリなソフトウェアの場合は、ベンダーに電話して叫びます(何も修正されませんが、気分が良くなります)。
MadHatterは、Monicaを

4
ドキュメントを確認してください。システム環境変数からのパスワードの読み取りがサポートされている場合があります。
エリオットフリッシュ

3
デーモンのコマンドラインでパスワードを指定しなくても、他のコマンドのコマンドラインでパスワードを指定することは依然として問題です。ps出力ではごく短時間しか表示されませんが、バックグラウンドで実行されているプロセスがそれを検出する可能性があります。しかし、パスワードを取得するのを難しくすることはもちろん価値があります。
カスペルド

2
この質問に対する答えを見てください。彼らはまさにこの問題に対処しています。
dotancohen

回答:


68

実際、これアプリケーション自体で修正する必要があります。そして、そのようなアプリケーションオープンソースである必要があります。そのため、アプリ自体の問題を修正することはオプションです。この種の間違いを犯すセキュリティ関連のアプリケーションは、他の間違いも犯すかもしれないので、私はそれを信用しません。

シンプルなインターポーザー

しかし、あなたは別の方法を求めていたので、ここに一つあります:

#define _GNU_SOURCE
#include <dlfcn.h>

int __libc_start_main(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  )
{
  int (*next)(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  ) = dlsym(RTLD_NEXT, "__libc_start_main");
  ubp_av[argc - 1] = "secret password";
  return next(main, argc, ubp_av, init, fini, rtld_fini, stack_end);
}

これをコンパイルします

gcc -O2 -fPIC -shared -o injectpassword.so injectpassword.c -ldl

次に、プロセスを実行します

LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start fakepasshrase

インターポーザーライブラリはmain、アプリケーションの関数が実行される前にこのコードを実行します。mainの呼び出しで、最後のコマンドライン引数を実際のパスワードに置き換えます。/proc/*/cmdlineただし、印刷されたコマンドライン(したがって、などのツールで表示されるps)には、まだ偽の引数が含まれています。明らかに、ソースコードとそれからコンパイルするライブラリを自分だけが読めるようにしなければならないので、chmod 0700ディレクトリで操作するのが最適です。また、パスワードはコマンド呼び出しの一部ではないため、bash履歴も安全です。

より高度なインターポーザー

より複雑な操作を行う場合は__libc_start_main、ランタイムライブラリが適切に初期化される前に実行されることに注意してください。したがって、絶対に不可欠でない限り、関数呼び出しを避けることをお勧めします。あなたの心のコンテンツに関数を呼び出すことができるようにしたい場合mainは、すべての初期化が完了した後、それが呼び出される直前に必ず呼び出してください。次の例では、コマンドライン引数として渡されたパスワードを非表示にする方法を指摘してくれたGrubermenschに感謝する必要がありましgetpassた。

#define _GNU_SOURCE
#include <dlfcn.h>
#include <unistd.h>

static int (*real_main) (int, char * *, char * *);

static int my_main(int argc, char * * argv, char * * env) {
  char *pass = getpass(argv[argc - 1]);
  if (pass == NULL) return 1;
  argv[argc - 1] = pass;
  return real_main(argc, argv, env);
}

int __libc_start_main(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  )
{
  int (*next)(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  ) = dlsym(RTLD_NEXT, "__libc_start_main");
  real_main = main;
  return next(my_main, argc, ubp_av, init, fini, rtld_fini, stack_end);
}

これによりパスワードの入力が求められるため、インターポーザライブラリを秘密にする必要がなくなりました。プレースホルダー引数はパスワードプロンプトとして再利用されるため、次のように呼び出します

LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start "Password: "

別の方法としては、ファイル記述子(例:gpg --passphrase-fddoなど)、またはからパスワードを読み取ることができますx11-ssh-askpass


4
私はコードを理解しておらず、テストすることもできませんが、その要点はわかります。これは実際の答えのように見え、トップの答えになるはずです。
マークヘンダーソン

これは本当に素晴らしいです。
ワカーリム

驚くばかり。私の知る限り、これは機能するはずです。もちろん、ソースにアクセスして再コンパイルできる必要があります。「文字列」などを使用する場合、パスワードはソースおよびコンパイルされたファイルで読み取り可能です。
トニー

1
STDINでパスワードを取得しても、この機能を使用してstrings脆弱性を除去することが可能です。SO:端末でのパスワード入力の非表示を参照してください。
グルーバーメンシュ

1
@ mulg0r:標準のextern "C"は、関連する関数の名前のマングリングを抑制するトリック、つまり__libc_start_main
MvG

28

歴史だけではありません。ps出力にも表示されます。

そのソフトウェアを書いた人は誰でも、掛けて、描き、四つ切りにしておくべきです。どのソフトウェアであるかに関係なく、コマンドラインでパスワードを指定する必要は絶対にありません。
デーモンプロセスの場合、それはさらに許されません...

ソフトウェア自体のrm -f以外に、これに対する解決策はわかりません。正直に言って、仕事を成し遂げる他のソフトウェアを見つけてください。そのようなジャンクを使用しないでください。


9
まったく役に立たないことをありがとう。これは長い間議論されてきたセキュリティ問題であり、まだ解決されておらず、今よりも良い回避策が必要rm -fです。
ワカーリム

17
実際、彼はとても親切です。引数としてパスフレーズを渡している場合、パスフレーズはに表示されpsます。したがって、開発者がそれを修正できるまで、彼は別のものを使用することを提案しています。
サファド

3
その後、別のオペレーティングシステムの作成を開始することをお勧めします。現在利用可能な他のソリューションはありません。神によって私はそれがあればいいのに。この問題を抱えているのはあなただけではありません。
トニー

8
せっかちな、うんざりしないでください。あなたはそれを小さな紙片の上に渡す方法を求めることができますが、それはそのような方法が自動的に存在することを意味しません。read_xは問題ありpsませんが、例えばを介してパスフレーズを公開するため、rmソリューションよりも優れています。
MadHatterは、Monica

7
y'allのが行くと、このない-本当に-答えに別の1を投げ、これが不可能であると文句を言う前に、私はあなたが見直し示唆下記MVGの答え
マーク・ヘンダーソン

19

これにより、ps出力がクリアされます。

非常に注意してください:これはアプリケーションを破壊する可能性があります。あなたはここにドラゴンであると正式に警告されています。

  • 外部プロセスがプロセスメモリをいじってはいけません。
  • プロセスがパスワードをこの地域に依存している場合、アプリケーションが破損する可能性があります。
  • これを行うと、そのプロセスで使用している作業データが破損する可能性があります。
  • これは非常識なハックです。

これで、これらの悲惨な警告が正式に通知されます。これにより、に表示された出力がクリアされpsます。履歴をクリアすることも、bashジョブの履歴(プロセスを実行するなどmyprocess myargs &)をクリアすることもありません。ただしps、引数は表示されなくなります。

#!/usr/bin/python
import os, sys
import re

PAGESIZE=4096

if __name__ == "__main__":
  if len(sys.argv) < 2:
    sys.stderr.write("Must provide a pid\n")
    sys.exit(1)

  pid = sys.argv[1]

  try:
    cmdline = open("/proc/{0}/cmdline".format(pid)).read(8192)

    ## On linux, at least, argv is located in the stack. This is likely o/s
    ## independent.
    ## Open the maps file and obtain the stack address.
    maps = open("/proc/{0}/maps".format(pid)).read(65536)
    m = re.search('([0-9a-f]+)-([0-9a-f]+)\s+rw.+\[stack\]\n', maps)
    if not m:
      sys.stderr.write("Could not find stack in process\n");
      sys.exit(1)

    start = int("0x"+m.group(1), 0)
    end = int("0x"+m.group(2), 0)

    ## Open the mem file
    mem = open('/proc/{0}/mem'.format(pid), 'r+')
    ## As the stack grows downwards, start at the end. It is expected
    ## that the value we are looking for will be at the top of the stack
    ## somewhere
    ## Seek to the end of the stack minus a couple of pages.
    mem.seek(end-(2*PAGESIZE))

    ## Read this buffer to the end of the stack
    stackportion = mem.read(8192)
    ## look for a string matching cmdline. This is pretty dangerous.
    ## HERE BE DRAGONS
    m = re.search(cmdline, stackportion)
    if not m:
      ## cause this is an example dont try to search exhaustively, just give up
      sys.stderr.write("Could not find command line in the stack. Giving up.")
      sys.exit(1)

    ## Else, we got a hit. Rewind our file descriptor, plus where we found the first argument.
    mem.seek(end-(2*PAGESIZE)+m.start())
    ## Additionally, we'll keep arg0, as thats the program name.
    arg0len = len(cmdline.split("\x00")[0]) + 1
    mem.seek(arg0len, 1)

    ## lastly overwrite the remaining region with nulls.
    writeover = "\x00" * (len(cmdline)-arg0len)
    mem.write(writeover)

    ## cleanup
    mem.close()

  except OSError, IOError:
    sys.stderr.write("Cannot find pid\n")
    sys.exit(1)

それを保存して、プログラムを呼び出しchmod +xます。./whatever <pidoftarget> これが機能する場合、出力は生成されません。失敗すると、何かについて不平を言って終了します。


18
。。。これは創造的で恐ろしいことです。
voretaq7 14年

EEK!今私は怖いです。
ジャンヌピッカライネン

Yikkes、それはうまくいくかもしれない... AppArmorのようなものがこれをキャッチするかどうかはわかりませんか?また、ウィルススキャナはこれを潜在的にキャッチし、「root」になる攻撃アカウントをブロックすることで大混乱を引き起こす可能性があります。ドラゴンは....確かに存在すること
Tonny

@Tonny保護されたドメインの場合、SELinuxはこれを防ぎます。基本的なUnix許可(DAC)には、この動作からの保護を提供するのに十分なサブジェクト粒度がありません(同じUID内のプロセスメモリの変更を許可します)。とにかく、それはバグではなく、機能です。これがgdb、実行中のプロセスのメモリを変更する方法だと思います(これよりもはるかに高い外科的精度で)。
マシューイフェ

11

rootまたは必要なユーザーのみがアクセスできるファイルから引数を渡すことはできますか?

コンソールにパスワードを入力するのは非常に難しいことですが、最後の手段は、履歴に表示されないようにスペースで行を開始することです。


それを有効にするシェルオプションがありましたが、デフォルトでは有効になっていないと思います。
heinrich5991

export HISTCONTROL=ignoreboth履歴に入力するための先頭スペースを持つ重複と行の両方を無視します。それを.bashrcまたは.bash_profileに追加します。
アンドレアス

7

たぶんこれは動作しますか(?):

darkcoind masternode start `cat password.txt`

3
またはdarkcoind masternode start `head -1`、パスワードを手動で入力する場合でも。
カスペルド14年

14
パスフレーズはps、および同様のユーティリティを介して引き続き利用できます。
voretaq7 14年

1
平文パスワードから平文パスワードに.bash_history移行するとpassword.txt、正確に何が得られますか?
MikeyB

1
@MikeyB:小さな勝利があります。誰かがあなたの肩越しに見ている間、あなたの歴史を検索している間に偶然それを公開することはありません。
MvG

1
@MikeyB、毎回そのファイルを作成および削除できます。
RiaD

4

残念ながら、darkcoindコマンドがパスワードをコマンドライン引数として予期している場合、パスワードはなどのユーティリティを通じて公開されますps。唯一の本当の解決策は、開発者教育することです

一方でps露出は避けられないかもしれない、あなたは、少なくともシェル履歴ファイルに書き込まれてからパスワードを保つことができます。

$ xargs darkcoind masternode start

password

CtrlD

履歴ファイルはxargs darkcoind masternode start、パスワードではなく、記録するだけです。


2
もしbashを使用している場合や、入れignorespace$HISTCONTROL、その後、あなたは防ぐことができます任意のスペースを使用してコマンドを付けることによって、シェル履歴に入るのコマンドを。
デロバート

3

他の人が述べたように、履歴から情報を隠すためにシェル履歴コントロールを調べてください。

しかし、まだ誰も示唆していないように思われることの1つは/prochidepidパラメーターを使用してマウントすることです。次のよう/proc/etc/fstabを含むhidepidように行を 変更してみてください:

# <file system> <mount point>   <type>  <options>       <dump>  <pass>
proc            /proc           proc    defaults,hidepid=2        0       0

2

新しいシェルプロセスからコマンドを実行することにより、シェルの履歴にパスワードを保存することができます。その後、すぐに終了します。例えば:

bash$ sh
sh$ darkcoind masternode start 'correct horse battery staple'
sh$ exit
bash$

履歴がファイルに保存されないshように設定されていることを確認してください。

もちろん、これは、パスワードがに表示されるなど、他の問題には対処しませんps。私は、darkcoindプログラム自体がから情報を隠す方法があると信じていますが、それpsは脆弱性のウィンドウを短くするだけです。


1
パスフレーズはps、および同様のユーティリティを介して引き続き利用できます。
voretaq7 14年

3
@ voretaq7:はい、答えの最後の段落で明示的に認めたとおりです。
キーストンプソン

3
確かに-あなたは私の側で不当なコピーパスタの犠牲者でした:)
voretaq7

2

ビットコインの場合、開発者の公式の答えはcontrib/bitrpc/bitrpc.pygithub)で提供されているPythonラッパーを使用することです:

walletpassphraseたとえば、コマンドを使用する場合、安全な方法でパスワードを要求します。にインタラクティブ機能を追加する予定はありませんbitcoin-cli

そして:

bitcoin-cli そのままで、インタラクティブな機能は得られません。

ソース:#2318

ウォレットのロック解除:

$ python bitrpc.py walletpassphrase

パスフレーズを変更:

$ python bitrpc.py walletpassphrasechange

https://github.com/bitcoin/bitcoin/tree/master/contrib/bitrpc

ダークコインの場合、それは次のように動作します:

https://github.com/darkcoin/darkcoin/tree/master/contrib/bitrpc

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.