Я запускаю программный демон, который требует для определенных действий ввести кодовую фразу, чтобы разблокировать некоторые функции, которые выглядят, например, так:
$ darkcoind masternode start <mypassphrase>
Теперь у меня возникли некоторые проблемы с безопасностью моего безголового сервера debian.
Когда я ищу свою историю bash, например, с помощью Ctrl+R
Я вижу этот сверхнадежный пароль. Теперь я представляю, что мой сервер скомпрометирован, и какой-то злоумышленник имеет доступ к оболочке и может просто Ctrl+R
найти мой пароль в истории.
Есть ли способ ввести кодовую фразу, чтобы она не отображалась в истории bash, ps
, /proc
или еще где-нибудь?
Обновление 1: Передача демону пароля без пароля вызывает ошибку. Это не вариант.
Обновление 2: Не говорите мне удалить программное обеспечение или другие полезные подсказки, например, вешать разработчиков. Я знаю, что это не лучший пример, но это программное обеспечение основано на биткойн и все клиенты, основанные на биткойнах, представляют собой своего рода json rpc-сервер, который слушает эти команды, и это известная проблема безопасности, которая все еще обсуждается (а, б, 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
?
Действительно, это должен быть исправлено в самом приложении. И такие приложения должен иметь открытый исходный код, поэтому исправление проблемы в самом приложении должно быть вариантом. Приложение, связанное с безопасностью, которое допускает такую ошибку, может допускать и другие ошибки, поэтому я бы ему не доверял.
Но вы просили другой способ, поэтому вот один:
#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-fd
делает), или из x11-ssh-askpass
, или что-то еще.
Дело не только в истории. Он появится в пс вывод тоже.
Тот, кто написал эту программу, должен быть повешен, нарисован и четвертован. Абсолютно НЕТ необходимости вводить пароль в командной строке независимо от того, какое это программное обеспечение.
Для процесса-демона это еще БОЛЬШЕ непростительно ...
Кроме rm -f на самом программном обеспечении я не знаю никакого решения для этого. Честно: найдите другое программное обеспечение для выполнения этой работы. Не используйте такой хлам.
Это очистит 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>
Если это сработает, результат не будет. В случае неудачи он на что-то пожалуется и уйдет.
Можете ли вы передать аргумент из файла, доступного только пользователю root или нужному пользователю?
ОГРОМНОЕ запрещение вводить пароли в консоли, но последний выход ... начинайте строку с пробела, чтобы она не отображалась в истории.
Может это сработает (?):
darkcoind masternode start `cat password.txt`
К сожалению, если ваш darkcoind
команда ожидает пароль в качестве аргумента командной строки, затем он будет доступен с помощью таких утилит, как ps
. Единственное реальное решение - обучать разработчиков.
В то время ps
раскрытие может быть неизбежным, вы могли бы, по крайней мере, предотвратить запись пароля в файл истории оболочки.
$ xargs darkcoind masternode start
паssшорd⏎
CtrlD
Файл истории должен записывать только xargs darkcoind masternode start
а не пароль.
Как заявляли другие, посмотрите в свой элемент управления историей оболочки, чтобы скрыть информацию из истории.
Но одной вещи, кажется, еще никто не предложил, это установить /proc
с hidepid
параметр. Попробуйте изменить свой /proc
линия в /etc/fstab
включать hidepid
, как это:
# <file system> <mount point> <type> <options> <dump> <pass>
proc /proc proc defaults,hidepid=2 0 0
Для Биткойна официальный ответ разработчика - использовать предоставленную оболочку 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
Для дарккойнов это работает в журнале:
https://github.com/darkcoin/darkcoin/tree/master/contrib/bitrpc
Вы можете сохранить пароль вне истории вашей оболочки, выполнив команду из нового процесса оболочки, который затем немедленно завершите. Например:
bash$ sh
sh$ darkcoind masternode start 'correct horse battery staple'
sh$ exit
bash$
Удостовериться sh
настроен не чтобы сохранить его историю в файл.
Конечно, это не решает других проблем, таких как отображение пароля в ps
. Я считаю, что есть способы darkcoind
сама программа, чтобы скрыть информацию от ps
, но это только сокращает окно уязвимости.