Python eval 利用技巧
Table of Contents
时间:2022-03-17
朋友分享了一道 Python 的 CTF 题,是关于存在 eval 参数可控,但长度受限的场景。完整代码无需贴出,我截取并修改产生漏洞的关键函数,代码如下:
# coding=utf-8 def multiply_func(x): print(f'value 1: {repr(x)}') v = input('value 2: ') if len(v) > 8: return return eval(f'{x} * {v}', {}, {}) if __name__ == '__main__': print(multiply_func(10))
multiply_func 函数体中,变量 v 的值取决于用户输入,因此是可控的,不过中间做了输入长度的判断限制——字符串长度不可大于 8。因此这里虽可执行任意代码,但代码体积又受限,无法很好地利用。
经过点拨,原来可以通过 Python 内置的 help 函数来获得执行任意系统命令:
1、输入:help(),这里字符串长度只有 6,会进入正常调用 eval 函数;
2、进入 help 交互式,然后输入任意一个模块名获得该模块的帮助文档,如 sys;
3、在 Linux 中,这里呈现帮助文档时,实际上是调用了系统里的 less 或 more 命令,可以利用这俩个命令执行本地命令的特性来获取一个 shell,继续按 #!,再执行外部命令 sh 即可。
1. help() 源码分析
为了弄清原理,在 Python 源码中找到 help 函数的实现,位于 Lib/_sitebuiltins.py 文件中的 _Helper 中类:
class _Helper(object): def __repr__(self): return "Type help() for interactive help, " \ "or help(object) for help about object." def __call__(self, *args, **kwds): import pydoc return pydoc.help(*args, **kwds)
_Helper 又调用了 pydoc.help 函数,位于 Lib/pydoc.py,其中的调用流程整理如下:
Helper.interact() -> Helper.help() -> doc() -> pager() -> getpager()
最后,getpager() 函数负责执行外部命令:
def getpager(): # ... 省略 ... if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: return lambda text: pipepager(text, 'less') import tempfile (fd, filename) = tempfile.mkstemp() os.close(fd) try: if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0: return lambda text: pipepager(text, 'more') else: return ttypager finally: os.unlink(filename)