명령 행 인수로 전달 된 비밀번호를 숨기는 방법은 무엇입니까?


43

예를 들어 다음과 같은 일부 기능을 잠금 해제하기 위해 암호 문구를 입력 해야하는 특정 작업이 필요한 소프트웨어 데몬을 실행 중입니다.

$ darkcoind masternode start <mypassphrase>

이제 헤드리스 데비안 서버에 대한 보안 문제가 있습니다.

예를 들어 bash 기록을 검색 할 때 Ctrl+R마다이 강력한 암호를 볼 수 있습니다. 이제 내 서버가 손상되고 일부 침입자가 셸 액세스 권한을 가지고 있으며 단순히 Ctrl+R역사에서 내 암호를 찾을 수 있다고 생각합니다 .

bash history 또는 ps, /proc또는 다른 곳 에서 비밀번호 문구를 표시하지 않고 입력하는 방법 이 있습니까?


업데이트 1 : 데몬에 암호를 전달하지 않으면 오류가 발생합니다. 이것은 옵션이 아닙니다.


업데이트 2 : 소프트웨어 또는 개발자 교수형과 같은 다른 유용한 힌트를 삭제하라고 말하지 마십시오. 나는 이것이 베스트 프랙티스 예 아니라는 것을 알고 있지만,이 소프트웨어를 기반으로 비트 코인 모든 비트 코인 기반 클라이언트는 여전히 논의되고 이러한 명령과 알려진 보안 문제 (수신 된 JSON RPC 서버의 일종이다 , B , C ) .


업데이트 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

4
설명서를 확인하십시오. 시스템 환경 변수에서 해당 비밀번호를 읽을 수 있습니다.
엘리엇 프리쉬

3
암호가 명령 행에서 디먼에 제공되지 않더라도 다른 명령의 명령 행에 암호를 제공하는 것은 여전히 ​​문제가됩니다. ps 출력에서만 매우 짧은 시간 동안 만 볼 수 있지만 백그라운드에서 실행중인 프로세스는 여전히이를 선택할 수 있습니다. 그러나 암호를 찾기가 더 어려워지는 것은 물론 가치가 있습니다.
kasperd

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-fddoes) 또는 x11-ssh-askpass, 또는 기타 에서 암호를 읽습니다 .


4
내가 이해하지 않고 코드를 테스트 할 수는 없지만, 나는 그것의 요점, 얻을 실제 대답 같은 외모를하고 상단의 해답이 될 것이다.
Mark Henderson

이것은 정말로 굉장하다.
Waqar Lim

대박. 내가 알 수있는 한이 작동해야합니다. 물론 소스에 액세스하여 다시 컴파일 할 수 있어야합니다. "문자열"또는 이와 유사한 것을 사용하는 경우 소스 및 컴파일 된 파일에서 비밀번호를 읽을 수 있으므로 다른 사람이 읽을 수 없도록하십시오.
Tonny

1
STDIN에서 암호를 가져와도 여전히 작동하므로 strings취약점 을 제거 할 수 있습니다. SO : 터미널에서 비밀번호 입력 숨기기를 참조하십시오 .
Grubermensch

1
@ mulg0r : 표준 extern "C" 는 관련 함수에 대한 이름 맹 글링을 억제하는 트릭을 수행해야합니다 __libc_start_main.
MvG

28

그것은 역사가 아닙니다. ps 출력에도 표시됩니다 .

이 소프트웨어를 작성한 사람은 매달고, 뽑아 내고 4 분의 1을해야합니다. 소프트웨어에 관계없이 명령 줄에 암호를 제공해야하는 것은 절대 아니요입니다.
데몬 프로세스의 경우 훨씬 더 용서할 수 없습니다 ...

소프트웨어 자체의 rm -f 외에도 이것에 대한 해결책을 모르겠습니다. 정직하게 : 다른 소프트웨어를 찾아서 작업을 완료하십시오. 그런 쓰레기를 사용하지 마십시오.


9
전혀 도움이되지 않아서 감사합니다. 이것은 오랫동안 논의 된 보안 문제 이며 여전히 해결되지 않았으며 rm -f지금 보다 더 나은 해결 방법이 필요합니다 .
와카 르 임

17
실제로 그는 매우 도움이되고 있습니다. 암호를 인수로 전달하면에 표시됩니다 ps. 따라서 개발자가 문제를 해결할 때까지 다른 것을 사용하는 것이 좋습니다.
Safado

3
그런 다음 다른 운영 체제를 작성하는 것이 좋습니다. 현재 알고있는 다른 솔루션은 없습니다. 하나님에 의해 하나 있었으면 좋겠다. 이 문제는 당신 만이 아닙니다.
Tonny

8
vertoe, 저격하지 마십시오. 작은 용지에 용지를 전달할 수있는 방법을 요청할 수 있지만 그렇다고 자동으로 존재하는 것은 아닙니다. read_x는 괜찮지 만 여전히 eg를 통해 암호 문구를 노출 ps하므로 rm솔루션 보다 낫지 않습니다 .
MadHatter

7
너희들이 가서이 없습니다 - 정말-AN-대답에 또 다른 하나를 던져이 불가능하다고 불평하기 전에, 당신이 검토 제안 아래 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

EEK! 이제 무서워
Janne Pikkarainen

Yikkes, 그것은 작동 할 수 있습니다 ... 나는 AppArmor가 이것을 잡을 것이라고 확신하지 못합니까? 또한 바이러스 스캐너는 잠재적으로이 문제를 포착하여 '루트'인 문제가있는 계정을 차단함으로써 혼란을 야기 할 수 있습니다. 실제로 용들이있다 ....
Tonny

@Tonny 보호 된 도메인의 경우 SELinux가이를 방지합니다. 기본 Unix 권한 (DAC)에는이 동작으로부터 보호 할 수있는 충분한 주제 단위가 없습니다 (동일한 UID 내에서 프로세스 메모리 수정 허용). 어쨌든 버그가 아니라 기능입니다. 나는 이것이 gdb실행중인 프로세스의 메모리를 수정하는 방법이라고 생각 합니다 (추가 할 수있는 것보다 훨씬 외과 적 정밀함).
Matthew Ife

11

루트 또는 필요한 사용자 만 액세스 할 수있는 파일에서 인수를 전달할 수 있습니까?

콘솔에 암호를 입력하는 것은 엄청나게 큰 일이지만 마지막 수단은 공백으로 줄을 시작하여 기록에 나타나지 않습니다.


그것을 가능하게하는 쉘 옵션이 있었지만 기본적으로 활성화되지 않았다고 생각합니다.
heinrich5991

export HISTCONTROL=ignoreboth히스토리 입력을위한 선행 공간이있는 중복 및 행을 모두 무시합니다. .bashrc 또는 .bash_profile에 추가하십시오.
Andreas

7

아마도 이것이 효과가 있습니까?

darkcoind masternode start `cat password.txt`

3
또는 darkcoind masternode start `head -1`비밀번호를 수동으로 입력하려는 경우 에도 마찬가지 입니다.
kasperd

14
암호는 여전히 ps유사한 유틸리티 를 통해 사용할 수 있습니다.
voretaq7

1
평문 암호를 평문 암호로 옮기면 정확히 무엇 .bash_historypassword.txt얻을 수 있습니까?
MikeyB

1
@MikeyB : 약간의 승리가 있습니다. 누군가가 당신의 어깨 너머로보고있는 동안 당신의 역사를 검색하는 동안 우연히 그것을 노출시키지 않을 것입니다.
MvG

1
@MikeyB, 당신은 매번 그 파일을 생성하고 제거 할 수 있습니다.
RiaD

4

불행히도, darkcoind명령에서 암호를 명령 줄 인수로 예상하면와 같은 유틸리티를 통해 노출됩니다 ps. 유일한 실제 솔루션은 개발자교육하는 것입니다 .

ps노출을 피할 수는 없지만 최소한 쉘 히스토리 파일에 비밀번호가 기록되지 않도록 할 수 있습니다.

$ xargs darkcoind masternode start

password

CtrlD

히스토리 파일은 xargs darkcoind masternode start비밀번호가 아닌을 기록해야 합니다.


2
당신이 떠들썩한 파티를 사용하는 경우 또는 넣어 ignorespace에서 $HISTCONTROL, 다음은 방지 할 수 있는 공간으로 명령을 앞에 붙여 쉘 역사에 들어 가지 명령을 사용합니다.
derobert

3

다른 사람들이 말했듯이, 기록에서 정보를 숨기려면 쉘 기록 컨트롤을 살펴보십시오.

그러나 아직 아무도 제안하지 않은 것 중 하나 /prochidepid매개 변수를 사용 하여 마운트 하는 것 입니다. 다음 과 같이 포함하도록 /proc줄을 수정하십시오 ./etc/fstabhidepid

# <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

3
@ voretaq7 : 예, 대답의 마지막 단락에서 명시 적으로 인정했습니다.
Keith Thompson

3
실제로-당신은 내 부분
wanton

2

Bitcoin의 공식 개발자 답변은 contrib/bitrpc/bitrpc.py( github ) 에 제공된 Python 래퍼를 사용하는 것입니다 .

walletpassphrase예를 들어 , 명령을 사용하면 안전한 방법으로 암호를 묻습니다 . 에 대화 형 기능을 추가 할 계획이 없습니다 bitcoin-cli.

과:

bitcoin-cli 대화 형 기능은 그대로 유지됩니다.

출처 : # 2318

지갑 잠금 해제 :

$ python bitrpc.py walletpassphrase

비밀번호 문구 변경 :

$ python bitrpc.py walletpassphrasechange

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

darkcoin의 경우 이상하게 작동합니다.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.