我正在运行一个软件守护进程,需要某些操作来input密码来解锁一些看起来像这样的function:
$ darkcoind masternode start <mypassphrase>
现在我在无头的debian服务器上遇到了一些安全问题。
每当我用Ctrl+R
search我的bash历史logging时,我可以看到这个超强的密码。 现在我想我的服务器已经被破坏,有些入侵者可以访问shell,只需Ctrl+R
就可以在历史logging中find我的密码。
有没有办法input密码,而不会显示在bash历史, ps
, /proc
或其他任何地方?
更新1 :没有密码守护进程将引发错误。 这是不行的。
更新2 :不要告诉我删除软件或其他有用的提示,如挂起开发人员。 我知道这不是一个最佳实践的例子,但这个软件是基于比特币的 ,所有基于比特币的客户端都是某种types的json rpc服务器,这些服务器监听这些命令,并且还讨论了一个已知的安全问题( 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
?
真的,这应该在应用程序本身中解决。 而这样的应用程序应该是开源的,所以解决在应用程序本身的问题应该是一个选项。 一个安全相关的应用程序,使这种错误也可能会犯其他错误,所以我不会相信它。
但是你要求一种不同的方式,所以这里是一个:
#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的实际密码中replace上一个命令行参数。 在/proc/*/cmdline
打印的命令行(因此可以通过ps
等工具查看)仍将包含假参数。 很明显,你必须把源代码和你编译的库只读给你自己,所以最好在chmod 0700
目录下运行。 而且由于密码不是命令调用的一部分,所以你的bash历史也是安全的。
如果你想做更详细的事情,你应该记住, __libc_start_main
在运行时库被正确初始化之前被执行。 所以我build议避免任何函数调用,除非它们是绝对必要的。 如果你想能够调用你的内容的函数,那么在main
被调用之前,确保你完成了所有的初始化。 对于下面的例子,我必须感谢Grubermensch指出如何隐藏作为命令行parameter passing的密码,这引起了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); }
这会提示input密码,所以您不再需要保持中介服务器库的安全。 占位符参数被重用为密码提示,所以像这样调用
LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start "Password: "
另一种方法是从文件描述符中读取密码(比如gpg --passphrase-fd
),或者从x11-ssh-askpass
或者其他的文件中读取密码。
这不只是历史。 它也将在ps输出中显示。
谁写这样的软件应该被挂起,被吸引和设置。 无论使用什么软件,在命令行上提供密码都是绝对不行的。
对于一个守护进程,它甚至是更不可原谅的…
除了软件本身的rm -f之外,我不知道任何解决scheme。 老实说:find其他软件来完成工作。 不要使用这样的垃圾。
这将清除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
it。 然后做./whatever <pidoftarget>
如果这个工作,它将不会产生输出。 如果失败了,就会抱怨某些东西而退出。
你可以通过一个文件的参数,只能由根或所需的用户访问?
在控制台input密码是一个巨大的禁忌,但最后的追求…开始你的线与一个空间,所以它不会出现在历史上。
也许这个工程(?):
darkcoind masternode start `cat password.txt`
不幸的是,如果你的darkcoind
命令希望密码作为命令行参数,那么它将通过诸如ps
实用程序暴露出来。 唯一真正的解决办法是教育开发人员 。
虽然ps
曝光可能是不可避免的,你至less可以保持在shell历史文件中写出的密码。
$ xargs darkcoind masternode start
p s s w o r d⏎
Ctrl D
历史文件应该只loggingxargs darkcoind masternode start
,而不是密码。
你可以通过执行一个新的shell进程的命令来让密码超出shell的历史logging,然后立即终止。 例如:
bash$ sh sh$ darkcoind masternode start 'correct horse battery staple' sh$ exit bash$
确保sh
被configuration为不保存文件中的历史logging。
当然这并不能解决其他的问题,比如在ps
中可以看到密码。 我相信, darkcoind
程序本身可以隐藏来自ps
的信息,但这只能缩短漏洞窗口。
正如其他人所说,看看你的shell历史控制隐藏历史信息。
但是有一点似乎没有人build议使用hidepid
参数来挂载/proc
。 尝试在/etc/fstab
修改/proc
行以包含hidepid
,如下所示:
# <file system> <mount point> <type> <options> <dump> <pass> proc /proc proc defaults,hidepid=2 0 0
对于比特币,官方开发者的答案是在contrib/bitrpc/bitrpc.py
( github )中使用提供的python包装:
例如,如果您使用命令
walletpassphrase
,它会以安全的方式要求input密码。 没有计划为bitcoin-cli
添加交互function。
和:
bitcoin-cli
将保持原样,不会获得交互function。
来源: #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