缘由:
在Freebuf上看到的两篇文章:Python eval的常见错误封装及利用原理、Python安全编码与代码审计,里面涉及到了对Python的eval函数的说明和讲解,之后我又去网上搜了一些资料,整理成了这篇文章。
搜索关键字:
python eval literal_eval
参考链接:
# Python中的eval是用来干嘛的?
- https://docs.python.org/2/library/functions.html#eval
- http://stackoverflow.com/questions/9383740/what-does-pythons-eval-do
# eval是非常危险的
- http://stackoverflow.com/questions/15197673/using-pythons-eval-vs-ast-literal-eval
- http://stackoverflow.com/questions/661084/security-of-pythons-eval-on-untrusted-strings
- http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
- http://effbot.org/zone/librarybook-core-eval.htm
- https://esdiscuss.org/topic/eval-literal-eval-safe-eval
# 用 eval / ast.literal_eval 来操作json数据?
- http://stackoverflow.com/questions/9949533/python-eval-vs-ast-literal-eval-vs-json-decode
- https://docs.python.org/2/library/ast.html#ast.literal_eval
# Python eval的常见错误封装及利用原理
参考解答:
问:Python中的eval是用来干嘛的?
答:eval() 将第一个字符串参数解释为Python代码并执行。
==
The eval function lets a python program run python code within itself.eval example (interactive shell):
>>> x = 1 >>> eval('x + 1') 2 >>> eval('x') 1
==
eval() interprets a string as code. The reason why so many people have warned you about using this is because a user can use this as an option to run code on the computer. If you have eval(input()) and os imported, a person could type into input() os.system(‘rm -R *’) which would delete all your files in your home directory. (Assuming you have a unix system). Using eval() is a security hole. If you need to convert strings to other formats, try to use things that do that, like int().
==
与eval()函数相比,ast.literal_eval()函数仅认为少数(安全的)Python语法操作是合法的:
The string or node provided may only consist of the following Python literal structures: strings, numbers, tuples, lists, dicts, booleans, and None.
Passing __import__(‘os’).system(‘rm -rf /’) into ast.literal_eval() will raise an error, but eval() will happily wipe your drive.
Since it looks like you’re only letting the user input a plain dictionary, use ast.literal_eval(). It safely does what you want and nothing more.
==
You cannot secure eval with a blacklist approach like this. See Eval really is dangerous for examples of input that will segfault the CPython interpreter, give access to any class you like, and so on.
==
In [1]: print eval("__import__('os').getcwd()", {}) D:\ In [2]: print eval("__import__('os').remove('file')", {"__builtins__": {}}) --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-2-ca308c631e67> in <module>() ----> 1 print eval("__import__('os').remove('file')", {"__builtins__": {}}) <string> in <module>() NameError: name '__import__' is not defined
Note that this doesn’t protect you from CPU or memory resource attacks (for example, something like:
>>> eval("'*'*1000000*2*2*2*2*2*2*2*2*2")
will most likely cause your program to run out of memory after a while)
==
下面这段代码则是退出解释器:
>>> s = """ ... [ ... c for c in ... ().__class__.__bases__[0].__subclasses__() ... if c.__name__ == "Quitter" ... ][0](0)() ... """ >>> eval(s, {'__builtins__':{}}) D:\>
初步理解一下整个过程:
>>> ().__class__.__bases__[0].__subclasses__() ... ...
这句Python代码的意思就是找tuple的class,再找它的基类,也就是object,再通过object找他的子类,具体的子类也如代码中的输出一样。从中可以看到了有file模块,zipimporter模块,是不是可以利用下呢?首先从file入手。
假如用户如果构造:
>>> s1 = """ ... [ ... c for c in ().__class__.__bases__[0].__subclasses__() ... if c.__name__ == "file" ... ][0]("/etc/passwd").read()() ... """ >>> eval(s1, {'__builtins__':{}}) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 5, in <module> IOError: file() constructor not accessible in restricted mode
这个restrictected mode简单理解就是Python解释器的沙盒,一些功能被限制了,比如说不能修改系统,不能使用一些系统函数,如file,详情见Restricted Execution Mode,那怎么去绕过呢?这时我们就想到了zipimporter了,假如引入的模块中引用了os模块,我们就可以进行利用了。也就是说刚才的safe_eval其实是不安全的。
如何正确使用
- 使用ast.literal_eval
- 如果仅仅是将字符转为dict,可以使用json格式
=EOF=
《 “Python中的eval” 》 有 10 条评论
python安全代码审计
http://joychou.org/index.php/web/python-sec-code-audit.html
[渗透测试]Python沙箱逃逸的n种姿势
https://xianzhi.aliyun.com/forum/read/2138.html
python audit tool 审计 注入
https://github.com/shengqi158/pyvulhunter
python安全和代码审计相关资料收集
https://github.com/bit4woo/python_sec
基于python的自动化代码审计
https://mp.weixin.qq.com/s/NXVo08FwtbWBxEcvuRwuLw
禁用import的情况下绕过python沙箱
https://www.anquanke.com/post/id/107000
https://zolmeister.com/2013/05/escaping-python-sandbox.html
https://mp.weixin.qq.com/s?__biz=MzIzOTQ5NjUzOQ==&mid=2247483665&idx=1&sn=4b18de09738fdc5291634db1ca2dd55a
Python 沙箱逃逸(payload总结)
http://foreversong.cn/archives/1201
一文看懂Python沙箱逃逸
https://paper.tuisec.win/detail/e358ffb6bda9720
https://www.freebuf.com/articles/system/203208.html
`
让用户提交 Python 代码并在服务器上执行,是一些 OJ、量化网站重要的服务,很多 CTF 也有类似的题。为了不让恶意用户执行任意的 Python 代码,就需要确保 Python 运行在沙箱中。沙箱经常会禁用一些敏感的函数,例如 os,研究怎么逃逸、防护这类沙箱还是蛮有意思的。
Python 的沙箱逃逸的最终目标就是执行系统任意命令,次一点的写文件,再次一点的读文件。
顺便安利一本书:《流畅的 Python》。这本书有很多中高阶知识点,很全面而且讲的很清楚,如果你看过,相信理解这篇文章的大多数内容都不是问题。
接下来的内容先讲系统命令执行,再讲文件写入、读取,并且均以 oj 为例,库大多以 os 为例。
`
利用OpCode绕过Python沙箱
https://xz.aliyun.com/t/6159
`
利用OpCode绕过Python沙箱
opcode又称为操作码,是将python源代码进行编译之后的结果,python虚拟机无法直接执行human-readable的源代码,因此python编译器第一步先将源代码进行编译,以此得到opcode。例如在执行python程序时一般会先生成一个pyc文件,pyc文件就是编译后的结果,其中含有opcode序列。
0x01 OpCode
如何查看一个函数的OpCode?
常见的字节码指令
变量
IF
CMP_OP
0x02 利用OpCode改变程序运行逻辑
Example1
Solution 1
Solution 2
Example 2
`
Python Security – 记录 Python 历史漏洞及补丁版本信息的 Repo
https://github.com/vstinner/python-security
https://python-security.readthedocs.io/