简单的插入器

简单的插入器

我正在运行一个软件守护程序,它要求输入密码才能解锁某些功能,例如如下所示:

$ darkcoind masternode start <mypassphrase>

现在我对我的无头 Debian 服务器的安全感到担忧。

每当我用搜索 bash 历史记录时,Ctrl+R我都能看到这个超强密码。现在我猜想我的服务器已被入侵,一些入侵者拥有 shell 访问权限,并且可以Ctrl+R在历史记录中找到我的密码。

有没有办法输入密码而不显示在 bash 历史记录ps/proc其他任何地方?


更新 1:如果不向守护进程传递密码,则会引发错误。这是没有选择的。


更新 2:不要告诉我删除软件或其他有用的提示,例如挂起开发人员。我知道这不是最佳实践的例子,但这个软件是基于比特币所有基于比特币的客户端都是某种 JSON RPC 服务器,用于监听这些命令,这是一个已知的安全问题,目前仍在讨论中(AbC)。


更新 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`

相关内容