Вопрос: Как скрыть пароль, переданный как аргумент командной строки?


Я запускаю программный демона, который требует, чтобы определенные действия вводили кодовую фразу, чтобы разблокировать некоторые функции, которые выглядят следующим образом:

$ darkcoind masternode start <mypassphrase>

Теперь у меня возникли проблемы с безопасностью на моем безголовом сервере debian.

Всякий раз, когда я ищу историю моего bash, например, с помощью Ctrl+R Я вижу этот супер надежный пароль. Теперь я полагаю, что мой сервер скомпрометирован, а у какого-то злоумышленника есть доступ к оболочке и может просто Ctrl+R найти мою кодовую фразу в истории.

Есть ли способ ввести кодовую фразу, чтобы она не отображалась в истории bash, ps, /proc или где-нибудь еще?


Обновление 1: Передача пароля демону не приводит к ошибке. Это не вариант.


Обновление 2: Не говорите мне, чтобы удалить программное обеспечение или другие полезные подсказки, как повешение разработчиков. Я знаю, что это не лучший пример, но это программное обеспечение основано на Bitcoin и все клиенты на основе биткойнов - это какой-то сервер json rpc, который слушает эти команды и известную проблему безопасности, которая все еще обсуждается (, б, с).


Обновление 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?


41
2018-05-02 15:30


Источник


Первый вопрос должен быть: что произойдет, если вы запустите демона без аргумента passphrase. Это просто подсказывает? - MadHatter
Я не думаю, что есть ответ, который будет работать. Неспособность запросить кодовую фразу - это главный недостаток в демоне. Если это бесплатное программное обеспечение, заведите программиста и исправьте его; не забудьте опубликовать свои изменения. Если это проприетарное программное обеспечение, позвоните продавцу и закричите на них (это ничего не исправит, но вам будет лучше). - MadHatter
Проверьте свою документацию, она может поддерживать чтение этого пароля из переменной системной среды. - Elliott Frisch
Даже если пароль не указан в командной строке для демона, все равно проблематично передать его в командной строке любой другой команды. Это видно только в выводе ps за очень короткое время, но процесс, выполняющийся в фоновом режиме, все равно может его забрать. Но, конечно, все же стоит сделать сложнее подобрать пароль. - kasperd
Посмотрите ответы на этот вопрос, они касаются именно этой проблемы. - dotancohen


Ответы:


Действительно, это должен фиксироваться в самой заявке. И такие приложения должен быть открытым исходным кодом, так что исправление проблемы в самом приложении должно быть опцией. Приложение, связанное с безопасностью, которое допускает такую ​​ошибку, может также допускать другие ошибки, поэтому я не буду этому доверять.

Простой мешатель

Но вы просили по-другому, так вот вот один:

#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 сам вызывается, после завершения инициализации. В следующем примере я должен поблагодарить Груберменша, который указал как скрыть пароль, переданный как аргумент командной строки который привел 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);
}

Это запрашивает пароль, поэтому вам больше не нужно хранить библиотеку промежуточного хранилища в секрете. Аргумент placeholder повторно используется как приглашение пароля, поэтому вызывайте это как

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

Другая альтернатива будет читать пароль из дескриптора файла (например, например, gpg --passphrase-fd делает), или из x11-ssh-askpass, или что-то еще.


64
2018-05-03 19:49



Хотя я не понимаю и не могу проверить код, я получаю его суть и это выглядит как реальный ответ и должен быть лучшим ответом. - Mark Henderson♦
Это действительно потрясающе. - Afri
Потрясающие. Насколько я могу судить, это должно сработать. Конечно, вам нужен доступ к источнику и возможность перекомпиляции. Пароль может быть прочитан в источнике и скомпилированном файле (файлах), если вы используете «строки» или что-то подобное, чтобы лучше убедиться, что никто не может их прочитать. - Tonny
Должна быть возможность взять пароль на STDIN и все еще иметь эту работу, которая удаляет strings уязвимость. Видеть SO: Скрыть ввод пароля на терминале, - Grubermensch
@Grubermensch. Вы правы. - Tonny


Это не просто история. Он появится в п.с. вывода.

Кто бы ни написал эту часть программного обеспечения, ее нужно повесить, нарисовать и расквартировать. Абсолютно НЕТ, чтобы предоставить пароль в командной строке, независимо от того, какое программное обеспечение оно есть.
Для процесса демона это еще БОЛЬШЕ непростительно ...

Кроме rm -f на самом программном обеспечении я не знаю никаких решений для этого. Честно: найдите другое программное обеспечение, чтобы выполнить работу. Не используйте такой мусор.


27
2018-05-02 15:41



Спасибо, что не помогли. Это долго обсуждалось проблема безопасности, все еще не решены, и мне нужно лучшее обходное решение, чем rm -fТеперь. - Afri
На самом деле, он очень полезен. Если вы передадите кодовую фразу в качестве аргумента, она появится в ps, Поэтому, пока разработчик не сможет это исправить, он предлагает использовать что-то еще. - Safado
Тогда вам лучше начать писать другую Операционную систему. В настоящее время нет никакого другого решения, которое я знаю. Клянусь Богом, я хочу, чтобы он был один. Вы не единственный с этой проблемой. - Tonny
Вертоле, не получится. Вы можете попросить способ передать его на маленьких листах бумаги, но это не означает, что такой способ автоматически существует. read_x отлично, но все же предоставляет кодовую фразу посредством, например, ps, поэтому он не лучше, чем rm решение. - MadHatter
Прежде чем вы пойдете и бросьте еще один +1 на этот не очень-ответ, и жалуйтесь, что это невозможно, я предлагаю вам рассмотреть Ответ MvG ниже - Mark Henderson♦


Это очистит 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> Если это сработает, это не приведет к выходу. Если он терпит неудачу, он будет жаловаться на что-то и уйти.


17
2018-05-02 22:04



, , , это одновременно и творческий, и пугающий. - voretaq7
EEK! Теперь я боюсь. - Janne Pikkarainen
Yikkes, это может сработать ... Я не уверен, что что-то вроде AppArmor поймает это? Кроме того, virusscanner может потенциально заразиться этим и привести к хаосу, заблокировав оскорбительную учетную запись, которая будет «root». На самом деле есть драконы ... - Tonny
@Tonny Для защищенных доменов SELinux предотвратит это. У ваших базовых разрешений Unix (DAC) недостаточно достаточной детализации, чтобы предлагать какие-либо защиты от этого поведения (разрешает модификацию памяти процессов в пределах того же UID). В любом случае, это не ошибка, а ее особенность. Я считаю, что это так gdb может изменить память запущенных процессов (с гораздо более высокой точностью, чем я мог бы добавить). - Matthew Ife


Можете ли вы передать аргумент из файла, доступного только root или требуемому пользователю?

HUGE no-no набирать пароли в консоли, но последнее обращение ... начинайте свою линию с пробела, чтобы она не появлялась в истории.


10
2018-05-02 16:33



Был вариант оболочки, который разрешает его, но я думаю, что он не был включен по умолчанию. - heinrich5991
export HISTCONTROL=ignoreboth игнорирует оба дубликата и строки с ведущим пространством для входа в историю. Добавьте его в свой .bashrc или .bash_profile. - Andreas


Может быть, это работает (?):

darkcoind masternode start `cat password.txt`

6
2018-05-02 19:31



Или даже darkcoind masternode start `head -1`, если вы хотите ввести пароль вручную. - kasperd
Кодовая фраза по-прежнему доступна через ps и аналогичные утилиты. - voretaq7
Переход от пароля открытого текста в .bash_history к паролю открытого текста в password.txt что вы получаете? - MikeyB
@MikeyB: Существует небольшая победа: вы случайно не разоблачите ее во время своей истории, пока кто-то смотрит через плечо. - MvG
@MikeyB, вы можете создавать и удалять этот файл каждый раз. - RiaD


К сожалению, если darkcoind команда ожидает пароль как аргумент командной строки, то он будет отображаться через утилиты, такие как ps, Единственное реальное решение обучать разработчиков,

В то время ps воздействие может быть неизбежным, вы могли бы хотя бы сохранить пароль из файла архива оболочки.

$ xargs darkcoind masternode start

пssвесорd

CtrlD

Файл истории должен записывать только xargs darkcoind masternode start, а не пароль.


3
2018-05-04 08:13



Или, если вы используете bash, поставьте ignorespace в $HISTCONTROL, а затем вы можете предотвратить Любые команды, перейдя в историю оболочки, префиксная команда с пробелом. - derobert


Вы можете оставить пароль из истории своей оболочки, выполнив команду из нового процесса оболочки, который затем немедленно прекратите. Например:

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

Убедиться sh настраивается не для сохранения его истории в файле.

Конечно, это не относится к другим проблемам, таким как пароль, видимый в ps, Есть, я считаю, способы для darkcoind сама программа, чтобы скрыть информацию от ps, но это только сокращает окно уязвимости.


1
2018-05-02 20:38



кодовая фраза по-прежнему доступна через ps и аналогичные утилиты. - voretaq7
@ voretaq7: Да, поскольку я был явно признан в последнем абзаце моего ответа. - Keith Thompson
Действительно - вы были жертвой бессмысленной копиипасты с моей стороны :) - voretaq7


Как указывали другие, загляните в свой контроль истории оболочки, чтобы скрыть информацию из истории.

Но никто, кажется, еще не предложил /proc с hidepid параметр. Попробуйте изменить /proc линии в /etc/fstab включать hidepid, как это:

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

1
2018-05-17 12:19





Для Bitcoin официальный ответ разработчика заключается в использовании предоставленной оболочки python в contrib/bitrpc/bitrpc.py (GitHub):

Он запрашивает пароль безопасным способом, если вы используете команду 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


1
2018-01-01 08:44