我正在运行一个软件守护程序,它要求输入密码才能解锁某些功能,例如如下所示:
$ darkcoind masternode start <mypassphrase>
现在我对我的无头 Debian 服务器的安全感到担忧。
每当我用搜索 bash 历史记录时,Ctrl+R
我都能看到这个超强密码。现在我猜想我的服务器已被入侵,一些入侵者拥有 shell 访问权限,并且可以Ctrl+R
在历史记录中找到我的密码。
有没有办法输入密码而不显示在 bash 历史记录ps
或/proc
其他任何地方?
更新 1:如果不向守护进程传递密码,则会引发错误。这是没有选择的。
更新 2:不要告诉我删除软件或其他有用的提示,例如挂起开发人员。我知道这不是最佳实践的例子,但这个软件是基于比特币所有基于比特币的客户端都是某种 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
?
答案1
真的,这应该可以在应用程序本身中修复。并且此类应用程序应该是开源的,因此在应用程序本身中修复问题应该是一种选择。犯这种错误的安全相关应用程序也可能犯其他错误,所以我不会信任它。
简单的插入器
但是你要求的是另一种方式,所以这里有一个:
#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
或其他地方读取密码。
答案2
这不仅仅是历史。它将出现在附言输出也是如此。
无论是谁编写了该软件,都应该被绞死、分尸。无论什么软件,在命令行上输入密码都是绝对不行的。
对于守护进程来说,这更是不可原谅的……
除了rm -f就软件本身而言,我不知道有什么解决办法。说实话:找其他软件来完成这项工作。不要使用这种垃圾。
答案3
这将清除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>
如果此操作有效,则不会产生任何输出。如果失败,它会抱怨某些事情并退出。
答案4
也许这有效(?):
darkcoind masternode start `cat password.txt`