非交互式流程的密码管理

时间:2015-12-20 11:40:43

标签: python security password-protection pid shlex

挑战

我需要一个密码管理工具,它将被其他进程调用(所有类型的脚本:python,php,perl等),它将能够识别和验证调用者脚本以执行访问控制:返回密码或退出-1

当前的实施

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

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

  1. 从它自己的PID开始递归看“up”并寻找父母。在我们到达父{0的-p进程1之前,必须找到调用者PID。这样我们就知道我们知道谁调用了这个密码管理器实例。
  2. 获取该(父)进程的完整命令行并分析它寻找脚本语言,包括python,perl,php,bash,bat,groovy等(init用于此)
  3. 找出脚本的绝对路径并计算其SHA
  4. 将其与数据库值进行比较并查看它是否存在,是否允许该脚本具有以标准格式在stdout中返回的密码。如果没有,请退出-1。
  5. 问题

    上述实现对于合法脚本很有效,但很容易混淆它。让shlex成为允许访问特定条目caller.py的脚本。运行命令行看起来像e。解析命令行的代码是:

    python /path/to/caller.py arg1 arg2

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

    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())
    

    现在,混淆上述功能的最简单方法是创建一个没有os.popen("ps -p %s -o args=" % ppid).read().strip() 扩展名的shell脚本,该扩展名作为.sh的第一个参数。 sh不使用其参数,而是调用密码管理器查询条目caller.py。命令行看起来像e,因此上面的代码返回传递...这是错误的事情。

    问题

    有人会认为这是很久以前解决的一个常见问题,没有程序员硬编码传递到脚本/应用程序,但我做了一些研究几天,我似乎找不到任何有用的东西类似的方式。我知道这个问题更开放,所以我会接受以下答案:

    • 我是否重新发明了轮子?是否有一个类似的框架/软件?
    • 这是正确的方法,依赖于PID吗?还有另一种方式吗?
    • 实施明智,发布的代码是否可以更强大,而不是那么容易混淆? (fake_sh ./caller.py分析部分)

1 个答案:

答案 0 :(得分:0)

改进:使规则更严格

第一步是确认在正确的解释器上运行正确的扩展名,这意味着caller.py无法在/bin/bash上运行。

类似的漏洞可以被python利用,例如 命令python -W ./caller.py ./myUberHack.py。查找解释器的第一个.py参数的命令行分析器会认为caller.py正在运行......哪个不是。

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

(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

目前想不出更好的事情......