非交互过程的密码管理 [英] Password Management for non-interactive process

查看:118
本文介绍了非交互过程的密码管理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要一个密码管理工具,该密码管理工具将由其他进程(各种脚本:python,php,perl等)调用,并且它能够识别和验证调用方脚本以执行访问控制:返回密码或退出-1

I need a password management tool that will be invoked by other processes (scripts of all sort: python, php, perl, etc) and it will be able to identify and verify the caller script in order to perform access control: either return a password back or exit -1

在研究了各种框架之后,我决定使用pythonkeepassdb能够处理Keepass V1.X后端数据库文件并构建我自己的访问控制覆盖图(因为以后可以对其进行自定义和集成)到我们的LDAP以进行用户/组访问).访问控制是通过重载每个条目的notes字段来完成的,以包括允许访问密码的SHA-256哈希列表. (请注意,这还可以验证该脚本未被任何人更改)

After looking into various frameworks, I have decided to use python's keepassdb which is able to handle Keepass V1.X backend database files and build my own access control overlay (since this can later be customized and integrated to our LDAP for user/group access). Access control is done via overloading the notes field of each entry to include a list of SHA-256 hashes that are allowed to access the password. (Note that this also validates that the script is not changed by anyone)

使用-p参数调用密码管理器,该参数是被调用方脚本/应用程序的PID,并将执行以下步骤:

The password manager is called with -p parameter which is the PID of the callee script/application and will do the following steps:

  1. 从其自己的PID开始递归查找"up"并寻找父母.在到达进程1(父进程为0的进程init)之前,必须找到调用者PID.通过这种方式,我们可以确定我们知道谁调用了此密码管理器实例.
  2. 获取该(父)进程的完整命令行并对其进行分析,以查找脚本语言,包括python,perl,php,bash,bat,groovy等(shlex用于此)
  3. 找出脚本的绝对路径并计算其SHA
  4. 将此与数据库值进行比较,看看是否存在,是否允许脚本具有以标准格式以stdout返回的密码.如果不是,请以-1退出.
  1. Look recursively "up" starting from its own PID and looking for parents. The caller PID has to be found before we reach process 1 which is init with parent 0. This way we are sure we know who called this password manager instance.
  2. Get the full command line of that (parent) process and analyse it looking for scripting languages including python, perl, php, bash, bat, groovy, etc (shlex is used for this)
  3. Figure out the absolute path of the script and calculate its SHA
  4. Compare this to the database values and see if it exists, if it does the script is allowed to have the password which is returned in stdout in a standard format. If not, exit with -1.

问题

上面的实现对于合法脚本很好地工作,但是很容易混淆它.假设caller.py是允许访问特定条目e的脚本.在命令行中运行它看起来像python /path/to/caller.py arg1 arg2.解析命令行的代码是:

The problem

The above implementation works nicely for legit scripts but it is very easy to confuse it. Let caller.py be a script that is allowed access to a specific entry e. Running it the command line looks like python /path/to/caller.py arg1 arg2. The code that parses the command line is:

cmd = walk_ppids(pid)
lg.debug(cmd)
if cmd is False:
    lg.error("PID %s is not my parent process or not a process at all!" % pid)
    sys.exit(-1)

cmd_parts = shlex.split(cmd)
running_script = ""
for p in cmd_parts:
    if re.search("\.(php|py|pl|sh|groovy|bat)$", p, re.I):
        running_script = p
        break

if not running_script:
    lg.error("Cannot identify this script's name/path!")
    sys.exit(-1)

running_script = os.path.abspath(running_script)
lg.debug("Found "+running_script)

phash = hash_file(open(running_script, 'rb'), hashlib.sha256())

使用以下命令获取父进程的命令行:

The command line of the parent process is acquired using:

os.popen("ps -p %s -o args=" % ppid).read().strip()

现在,最容易混淆上述功能的方法是创建一个不带.sh扩展名的shell脚本,该扩展名将caller.py作为第一个参数. sh不使用其参数,而是调用密码管理器查询条目e.命令行看起来像fake_sh ./caller.py,因此上面的代码返回pass ...这是错误的事情.

Now, the easiest way to confuse the above function is to create a shell script without the .sh extension that takes as first argument the caller.py. The sh does not use its arguments, instead it invokes the password manager querying for the entry e. The command line would look like fake_sh ./caller.py and thus the above code returns the pass... which is the wrong thing to do.

有人认为这是很久以前就解决的一个常见问题,无需程序员将硬编码传递到脚本/应用程序中,但是我花了几天的时间进行了一些研究,但似乎无法在其中找到任何有效的方法.类似的方式.我知道这个问题比较开放,因此我将接受以下答案:

One would assume that this is a common problem solved long time ago without programmers hard-coding passes into scripts/apps but I did a bit of research for few days and I didn't seem to able to find anything that works in similar way. I understand that this question is more open-ended so I will accept answers to the following:

  • 我是在重新发明轮子吗?有没有可以做类似事情的框架/软件?
  • 这是依靠PID的正确方法吗?还有另一种方法吗?
  • 在实施方面明智的做法是,是否可以将发布的代码进行改进以使其更健壮并且不那么容易混淆? (shlex分析部分)
  • Am I re-inventing the wheel? Is there a framework/software that will do something similar?
  • Is this the correct approach, relying on PIDs? Is there another way?
  • Implementation wise, could the code posted be improved to be more robust and not that easily confused? (shlex analysis part)

推荐答案

改进:使规则更加严格

第一步是确认正确的扩展程序在正确的解释器上运行,这意味着caller.py不能在/bin/bash上运行.

The first step was to confirm that the correct extension runs on the correct interpreter which means that caller.py cannot run on /bin/bash.

类似的漏洞可以通过python利用,例如 命令python -W ./caller.py ./myUberHack.py.在命令行分析器中寻找解释器的第一个.py参数时,会认为caller.py正在运行...而事实并非如此.

Similar vulnerabilities can be exploited with python, for example the command python -W ./caller.py ./myUberHack.py. A command line analyzer that looks for the 1st .py argument to the interpreter will think that caller.py is running... which is not.

为所有解释器构建所有调用规则将非常耗时,因此我对这些假设进行了硬编码.这些存储在tuple中,每行是:

Building all the invocation rules for all interpreters would be too time consuming, so I hard-code the assumptions. These are store in a tuple and each line is:

(file extension, positional argument, interpreter first letters)

exts = (
    (".py", 1, "python"), 
    (".php", 2, "php"),
    (".pl", 1, "perl"),
    (".sh", 1, "/bin/bash"), # Assumption, we accept only bash 
    (".groovy", 1, "groovy"),
    (".rb", 1, "ruby"),
)
"""Matching extensions to positional arguments and interpreters"""

现在的验证码是:

for i in exts:
    # Check the specified cmdline position and extension
    if cmd_parts[i[1]].strip().endswith(i[0]):
        lg.debug("Checking "+cmd_parts[i[1]])
        running_script = cmd_parts[i[1]]

        # Make sure that the interpretter matches the extension
        if running_script.endswith(i[0]) and not cmd_parts[0].startswith(i[2]):
            lg.error("Wrong interpretter... go away...")
            sys.exit(-1)

        break

目前想不出更好的方法...

Can't think of anything better at the moment...

这篇关于非交互过程的密码管理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆