Як приховати пароль, переданий як аргумент командного рядка?


43

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

$ darkcoind masternode start <mypassphrase>

Тепер у мене виникли певні проблеми щодо безпеки на моєму безголовому сервері debian.

Щоразу, коли я шукаю свою історію баш, наприклад, Ctrl+Rя можу побачити цей надзвичайно сильний пароль. Тепер я думаю, що мій сервер зламаний, і якийсь зловмисник має доступ до оболонки і може просто Ctrl+Rзнайти мою парольну фразу в історії.

Чи є спосіб , щоб ввести ключову фразу без нього буде показано в Баш історії ps, /procабо де - небудь ще?


Оновлення 1 : Передача паролю демону не призводить до помилки. Це не варіант.


Оновлення 2 : Не кажіть мені видаляти програмне забезпечення чи інші корисні підказки, як, наприклад, вивішування розробників. Я знаю, що це не найкращий приклад, але це програмне забезпечення базується на біткоіні, і всі клієнти на базі біткойна - це якийсь сервер rjs-сервера json, який слухає ці команди та відому проблему безпеки, яка все ще обговорюється ( a , 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каталозі. А оскільки пароль не є частиною виклику команди, ваша історія башів також безпечна.

Більш просунутий інтерпозер

Якщо ви хочете зробити щось більш досконале, слід пам’ятати, що воно __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, або будь-якого іншого.


4
Хоча я не розумію і не можу перевірити код, я розумію його суть, і це виглядає як реальна відповідь і має бути головною відповіддю.
Марк Хендерсон

Це справді приголомшливо.
Вакар Лім

Дивовижно. Наскільки я можу сказати, це повинно працювати. Звичайно, вам потрібен доступ до джерела та зможете перекомпілювати. Пароль доступний для читання у джерелі та у зібраному файлі (файлах), якщо ви використовуєте "рядки" чи щось подібне, тому краще переконайтеся, що ніхто більше не може їх прочитати.
Тонні

1
Потрібно мати можливість взяти пароль на STDIN і все ще мати цю роботу, що знімає stringsвразливість. Див. ТАК: Сховати введення пароля на терміналі .
Grubermensch

1
@ mulg0r: Стандартний зовнішній вигляд "C" повинен зробити трюк, щоб придушити ім'я mangling для відповідної функції, а саме __libc_start_main.
MvG

28

Це не лише історія. Він також відображатиметься в PS- випуску.

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

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


9
Дякуємо, що зовсім не корисні. Це довго обговорюване питання безпеки , досі не вирішене , і мені потрібен кращий шлях, ніж rm -fзараз.
Вакар Лім

17
Насправді він дуже допомагає. Якщо ви передаєте парольну фразу як аргумент, вона з’явиться у ps. Тож поки розробка не зможе це виправити, він пропонує використовувати щось інше.
Сафадо

3
Тоді краще почати писати іншу операційну систему. В даний час немає жодного іншого рішення, яке мені відомо. Я б хотів, щоб Бог був. Ви не єдиний із цією проблемою.
Тонні

8
vertoe, не сприймай. Ви можете попросити спосіб передати його на невеликих шматочках паперу, але це не означає, що такий спосіб автоматично не існує. read_x чудово, але все-таки виставляє парольну фразу через, наприклад ps, тому це не краще, ніж rmрішення.
MadHatter підтримує Моніку

7
Перш ніж піти і кинути ще +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

EEK! Зараз мені страшно.
Janne Pikkarainen

Yikkes, це могло б спрацювати ... Я не впевнений, що щось подібне до AppArmor це вловить? Також вірус-сканер може потенційно наздогнати це і спричинити хаос, заблокувавши обліковий запис правопорушника, який був би "root". Там справді будуть дракони ....
Тонні

@Tonny Для захищених доменів SELinux запобігає цьому. У ваших базових дозволах Unix (DAC) бракує достатньої деталізації, щоб забезпечити будь-який захист від такої поведінки (дозволяє змінювати пам'ять процесів у межах одного UID). У будь-якому разі, це не помилка - її особливість. Я вважаю, що саме так gdbможна змінити пам'ять запущених процесів (з набагато більшою точністю хірургічної точності, ніж я можу додати).
Метью Іфе

11

Чи можете ви передати аргумент з файлу, доступного лише корінцю або потрібному користувачеві?

Вводити паролі в консоль ВЕЛИЧЕЗНО-ні, але останній засіб ... почніть рядок з пробілу, щоб він не відображався в історії.


Була опція оболонки, яка це дозволяє, але я думаю, вона не була включена за замовчуванням.
heinrich5991

export HISTCONTROL=ignorebothігнорує як дублікати, так і рядки з провідним місцем для входу в історію. Додайте його до свого .bashrc або .bash_profile.
Андреас

7

Можливо, це працює (?):

darkcoind masternode start `cat password.txt`

3
Або навіть darkcoind masternode start `head -1`, якщо ви хочете ввести пароль вручну.
kasperd

14
Парольна фраза все ще доступна через psта подібні утиліти.
voretaq7

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
Або , якщо ви використовуєте Баш, покласти ignorespaceв $HISTCONTROL, а потім ви можете запобігти будь-яку команду з вдаючись в історію оболонки, випередивши команду з пропуском.
дероберт

3

Як уже зазначали інші, перегляньте свій контроль історії оболонки, щоб приховати інформацію з історії.

Але одна річ , здається , ніхто не запропонував ще є монтування /procз hidepidпараметром. Спробуйте змінити /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

3
@ voretaq7: Так, як я прямо визнав в останньому абзаці своєї відповіді.
Кіт Томпсон

3
Дійсно - ви стали жертвою невідомого copypasta з мого боку :)
voretaq7

2

Для 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.