non-interactive 进程的密码管理
Password Management for non-interactive process
挑战
我需要一个密码管理工具,它将被其他进程(各种脚本:python、php、perl 等)调用,并且能够识别和验证密码调用者脚本以执行访问控制:return 返回密码或退出 -1
当前实现
在研究了各种框架之后,我决定使用 python
的 keepassdb
,它能够处理 Keepass V1.X 后端数据库文件,并构建我自己的访问控制覆盖层(因为稍后可以对其进行自定义并集成到我们的 LDAP 中以进行 user/group 访问)。访问控制是通过重载每个条目的 notes
字段来完成的,以包含允许访问密码的 SHA-256 哈希列表。 (注意,这也验证了脚本没有被任何人更改)
使用 -p
参数调用密码管理器,该参数是被调用者的 PID script/application,并将执行以下步骤:
- 递归查找"up" 从它自己的 PID 开始并查找 parent。在我们到达进程
1
之前必须找到调用者 PID,它是 init
和 parent 0。这样我们就可以确定我们知道谁调用了这个密码管理器实例。
- 获取该 (parent) 进程的完整命令行并对其进行分析以寻找脚本语言,包括 python、perl、php、bash、bat、 groovy,等(
shlex
用于此)
- 找出脚本的绝对路径并计算其 SHA
- 将它与数据库值进行比较,看看它是否存在,如果存在,则允许脚本使用 return 以标准格式在 stdout 中编辑的密码。如果不是,则以 -1 退出。
问题
上面的实现对于合法的脚本来说效果很好,但是很容易混淆。设 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())
parent 进程的命令行是通过以下方式获取的:
os.popen("ps -p %s -o args=" % ppid).read().strip()
现在,混淆上述函数的最简单方法是创建一个没有 .sh
扩展名的 shell 脚本,该脚本将 caller.py
作为第一个参数。 sh 不使用其参数,而是调用密码管理器查询条目 e
。命令行看起来像 fake_sh ./caller.py
,因此上面的代码 return 是通过...这是错误的做法。
问题
人们会认为这是很久以前没有程序员解决的一个常见问题 hard-coding 传递给 scripts/apps 但我做了几天的研究但我似乎无法找到任何以类似方式工作的东西。我明白这个问题比较open-ended所以我会接受以下答案:
- 我是re-inventing轮子吗?是否有 framework/software 可以做类似的事情?
- 这是依赖 PID 的正确方法吗?还有别的办法吗?
- 在实现方面,发布的代码是否可以改进为更健壮且不易混淆? (
shlex
分析部分)
改进:使规则更加严格
第一步是确认正确的解释器 运行 的扩展名是否正确,这意味着 caller.py
不能 运行 在 /bin/bash
上。
python 可以利用类似的漏洞,例如
命令 python -W ./caller.py ./myUberHack.py
。查找解释器的第一个 .py
参数的命令行分析器会认为 caller.py
是 运行ning... 而不是。
为所有解释器构建所有调用规则太费时了,所以我硬编码了这些假设。这些存储在 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
目前想不出更好的...
挑战
我需要一个密码管理工具,它将被其他进程(各种脚本:python、php、perl 等)调用,并且能够识别和验证密码调用者脚本以执行访问控制:return 返回密码或退出 -1
当前实现
在研究了各种框架之后,我决定使用 python
的 keepassdb
,它能够处理 Keepass V1.X 后端数据库文件,并构建我自己的访问控制覆盖层(因为稍后可以对其进行自定义并集成到我们的 LDAP 中以进行 user/group 访问)。访问控制是通过重载每个条目的 notes
字段来完成的,以包含允许访问密码的 SHA-256 哈希列表。 (注意,这也验证了脚本没有被任何人更改)
使用 -p
参数调用密码管理器,该参数是被调用者的 PID script/application,并将执行以下步骤:
- 递归查找"up" 从它自己的 PID 开始并查找 parent。在我们到达进程
1
之前必须找到调用者 PID,它是init
和 parent 0。这样我们就可以确定我们知道谁调用了这个密码管理器实例。 - 获取该 (parent) 进程的完整命令行并对其进行分析以寻找脚本语言,包括 python、perl、php、bash、bat、 groovy,等(
shlex
用于此) - 找出脚本的绝对路径并计算其 SHA
- 将它与数据库值进行比较,看看它是否存在,如果存在,则允许脚本使用 return 以标准格式在 stdout 中编辑的密码。如果不是,则以 -1 退出。
问题
上面的实现对于合法的脚本来说效果很好,但是很容易混淆。设 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())
parent 进程的命令行是通过以下方式获取的:
os.popen("ps -p %s -o args=" % ppid).read().strip()
现在,混淆上述函数的最简单方法是创建一个没有 .sh
扩展名的 shell 脚本,该脚本将 caller.py
作为第一个参数。 sh 不使用其参数,而是调用密码管理器查询条目 e
。命令行看起来像 fake_sh ./caller.py
,因此上面的代码 return 是通过...这是错误的做法。
问题
人们会认为这是很久以前没有程序员解决的一个常见问题 hard-coding 传递给 scripts/apps 但我做了几天的研究但我似乎无法找到任何以类似方式工作的东西。我明白这个问题比较open-ended所以我会接受以下答案:
- 我是re-inventing轮子吗?是否有 framework/software 可以做类似的事情?
- 这是依赖 PID 的正确方法吗?还有别的办法吗?
- 在实现方面,发布的代码是否可以改进为更健壮且不易混淆? (
shlex
分析部分)
改进:使规则更加严格
第一步是确认正确的解释器 运行 的扩展名是否正确,这意味着 caller.py
不能 运行 在 /bin/bash
上。
python 可以利用类似的漏洞,例如
命令 python -W ./caller.py ./myUberHack.py
。查找解释器的第一个 .py
参数的命令行分析器会认为 caller.py
是 运行ning... 而不是。
为所有解释器构建所有调用规则太费时了,所以我硬编码了这些假设。这些存储在 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
目前想不出更好的...