Afr*_*Afr 43 security linux password command-line-interface
我正在运行一个软件守护程序,它需要某些操作来输入密码来解锁某些功能,例如:
$ darkcoind masternode start <mypassphrase>
Run Code Online (Sandbox Code Playgroud)
现在我在我的无头 debian 服务器上遇到了一些安全问题。
例如,每当我搜索我的 bash 历史记录时,Ctrl+R我都可以看到这个超强密码。现在我想我的服务器被入侵了,一些入侵者可以访问 shell,并且可以简单地Ctrl+R在历史记录中找到我的密码。
有没有办法输入密码而不显示在 bash 历史记录ps、/proc或其他任何地方?
更新 1:不向守护程序传递密码会引发错误。这是没有办法的。
更新 2:不要告诉我删除软件或其他有用的提示,例如挂掉开发人员。我知道这不是最佳实践示例,但该软件基于比特币,并且所有基于比特币的客户端都是某种 json rpc 服务器,它侦听这些命令,并且它的已知安全问题仍在讨论中(a、b、c) .
更新 3:守护进程已经启动并使用命令运行
$ darkcoind -daemon
Run Code Online (Sandbox Code Playgroud)
做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
Run Code Online (Sandbox Code Playgroud)
因此,使用密码短语传递命令不会显示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
Run Code Online (Sandbox Code Playgroud)
这留下了历史出现在哪里的问题?只有在.bash_history?
MvG*_*MvG 73
真的,这应该在应用程序本身中修复。此类应用程序应该是开源的,因此可以选择解决应用程序本身的问题。犯这种错误的安全相关应用程序也可能犯其他错误,所以我不会相信它。
但是你要求的是一种不同的方式,所以这里有一个:
#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);
}
Run Code Online (Sandbox Code Playgroud)
编译这个
gcc -O2 -fPIC -shared -o injectpassword.so injectpassword.c -ldl
Run Code Online (Sandbox Code Playgroud)
然后运行你的过程
LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start fakepasshrase
Run Code Online (Sandbox Code Playgroud)
中介层库将在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);
}
Run Code Online (Sandbox Code Playgroud)
这会提示您输入密码,因此您不再需要对内插器库保密。占位符参数被重用为密码提示,所以像这样调用
LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start "Password: "
Run Code Online (Sandbox Code Playgroud)
另一种选择是从文件描述符(例如gpg --passphrase-fddo)或 fromx11-ssh-askpass或其他任何地方读取密码。
Ton*_*nny 28
这不仅仅是历史。它也会出现在ps输出中。
编写该软件的人应该被悬挂、绘制和分割。无论是什么软件,都绝对不能在命令行上提供密码。
对于守护进程,它甚至更不可原谅......
除了软件本身的rm -f之外,我不知道任何解决方案。老实说:寻找其他软件来完成工作。不要用这种垃圾。
Mat*_*Ife 20
这将清除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)
Run Code Online (Sandbox Code Playgroud)
通过保存它来调用程序chmod +x。然后做./whatever <pidoftarget>
如果这有效,它将不会产生任何输出。如果失败,它会抱怨某些事情并退出。
也许这有效(?):
darkcoind masternode start `cat password.txt`
Run Code Online (Sandbox Code Playgroud)
不幸的是,如果您的darkcoind命令需要密码作为命令行参数,那么它将通过诸如ps. 唯一真正的解决办法是教育开发人员。
虽然ps暴露可能是不可避免的,但您至少可以防止密码被写入 shell 历史文件中。
\n\n\n\n\n
$ xargs darkcoind masternode startpassword\xe2\x8f\x8e
\n\nCtrlD
\n
历史文件应该只记录xargs darkcoind masternode start,而不是密码。