eval()、exec()与execfile()
eval(expression[, globals[, locals]])
eval()函数执行一个python表达式字符串并返回表达式执行后的结果:
1 2 3 | >>> x = 1 >>> eval ( 'x + 1' ) 2 |
expression参数为一个表达式字符串,globals和locals为可选的参数,globals必须是一个字典对象,locals可为任意映射对象,分别用作代码执行的全局和局部命名空间。
globals和locals参数缺失的时候,表达式会使用当前环境的全局和局部命名空间值:
1 2 3 4 5 6 | >>> x = 1 >>> g = { 'x' : 2 } >>> eval ( 'x + 1' , g) 3 >>> eval ( 'x + 1' ) 2 |
eval()函数在数据类型转换中很有用,可以将字符串转换成字典、列表等:
1 2 3 4 5 6 | >>> exp = '[1,2,3,4]' >>> eval (exp) [ 1 , 2 , 3 , 4 ] >>> exp = '{"a" : 1}' >>> eval (exp) { 'a' : 1 } |
eval()也可以使用模块:
1 2 3 | >>> import os >>> eval ( 'os.getcwd()' ) '/home/user' |
当然,eval不能直接对模块进行操作,如果非要使用eval进行import:
1 2 | >>> eval ( '__import__("os").getcwd()' ) '/home/user' |
这一般用于根据客户需求动态的调用不同的模块。
其它的用做计算器啥的也不在话下,不过eval函数也不能滥用,比如要获取用户的输入并求值eval(raw_input()),
这种情况下,用户输入open(__file__).read()可直接把原文件读出来了。
要安全的使用eval()函数,可以使用globals和locals两个参数来设置白名单,当参数缺失的时候,表达式会使用当前环境的全局和局部命名空间值,即globals()和locals()中包含的模块和函数:
1 2 3 4 5 | >>> import os >>> 'os' in globals () True >>> eval ( 'os.getcwd()' ) '/home/user' |
将globals和locals两个参数设置为空:
1 2 3 4 5 6 | >>> import os >>> eval ( 'os.getcwd()' , {}, {}) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> File "<string>" , line 1 , in <module> NameError: name 'os' is not defined |
但使用内置函数却可以绕过白名单:
1 2 3 4 | >>> eval ( 'abs(10)' , {}, {}) 10 >>> eval ( '__import__("os").getcwd()' , {}, {}) '/home/user' |
可以看下globals():
1 2 | >>> globals () { '__builtins__' : <module '__builtin__' (built - in )>, '__name__' : '__main__' , '__doc__' : None , '__package__' : None } |
__builtins__设置着python的内置函数,下面的写法是一样的:
1 2 3 4 | >>> abs ( 10 ) 10 >>> __builtins__. abs ( 10 ) 10 |
将__builtins__设置为空则可避免内置函数的滥用:
1 2 3 4 5 | >>> eval ( 'abs(10)' , { '__builtins__' : None }, {}) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> File "<string>" , line 1 , in <module> NameError: name 'abs' is not defined |
看情况这下是安全了,但是还是可以绕过的:
1 2 | >>> ().__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 'PyCapsule' >, < type 'cell' >, < type 'callable-iterator' >, < type 'iterator' >, < type 'sys.long_info' >, < type 'sys.float_info' >, < type 'EncodingMap' >, < type 'fieldnameiterator' >, < type 'formatteriterator' >, < type 'sys.version_info' >, < type 'sys.flags' >, < type 'sys.getwindowsversion' >, < type 'exceptions.BaseException' >, < type 'module' >, < type 'imp.NullImporter' >, < type 'zipimport.zipimporter' >, < type 'nt.stat_result' >, < type 'nt.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 'operator.itemgetter' >, < type 'operator.attrgetter' >, < type 'operator.methodcaller' >, < type 'functools.partial' >, < type 'MultibyteCodec' >, < type 'MultibyteIncrementalEncoder' >, < type 'MultibyteIncrementalDecoder' >, < type 'MultibyteStreamReader' >, < type 'MultibyteStreamWriter' >, < type 'time.struct_time' >, < type '_ssl._SSLContext' >, < type '_ssl._SSLSocket' >, < type 'cStringIO.StringO' >, < type 'cStringIO.StringI' >, < class 'socket._closedsocket' >, < type '_socket.socket' >, < type 'method_descriptor' >, < class 'socket._socketobject' >, < class 'socket._fileobject' >, < type '_thread._localdummy' >, < type 'thread._local' >, < type 'thread.lock' >, < type 'collections.deque' >, < type 'deque_iterator' >, < type 'deque_reverse_iterator' >, < type 'itertools.combinations' >, < type 'itertools.combinations_with_replacement' >, < type 'itertools.cycle' >, < type 'itertools.dropwhile' >, < type 'itertools.takewhile' >, < type 'itertools.islice' >, < type 'itertools.starmap' >, < type 'itertools.imap' >, < type 'itertools.chain' >, < type 'itertools.compress' >, < type 'itertools.ifilter' >, < type 'itertools.ifilterfalse' >, < type 'itertools.count' >, < type 'itertools.izip' >, < type 'itertools.izip_longest' >, < type 'itertools.permutations' >, < type 'itertools.product' >, < type 'itertools.repeat' >, < type 'itertools.groupby' >, < type 'itertools.tee_dataobject' >, < type 'itertools.tee' >, < type 'itertools._grouper' >, < class 'threading._Verbose' >, < class 'string.Template' >, < class 'string.Formatter' >, < type 'CArgObject' >, < type '_ctypes.CThunkObject' >, < type '_ctypes._CData' >, < type '_ctypes.CField' >, < type '_ctypes.DictRemover' >, < type 'Struct' >, < class 'ctypes.CDLL' >, < class 'ctypes.LibraryLoader' >, < type 'cPickle.Unpickler' >, < type 'cPickle.Pickler' >, < class 'idlelib.rpc.SocketIO' >, < class 'idlelib.rpc.RemoteObject' >, < class 'idlelib.rpc.RemoteProxy' >, < class 'idlelib.rpc.RPCProxy' >, < class 'idlelib.rpc.MethodProxy' >, < type '_io._IOBase' >, < type '_io.IncrementalNewlineDecoder' >, < class 'subprocess.Popen' >, < class 'webbrowser.BaseBrowser' >, < class 'idlelib.tabbedpages.Page' >, < class 'idlelib.EditorWindow.HelpDialog' >, < type '_hashlib.HASH' >, < type '_random.Random' >, < class 'idlelib.EditorWindow.EditorWindow' >, < class 'idlelib.EditorWindow.IndentSearcher' >, < class 'idlelib.run.Executive' >] |
通过tuple的class找到它的基类,也就是object,然后再找到object的各种子类,从中可以看到很多模块。
使用Quitter退出解释器:
1 2 | >>> eval ( "[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__== 'Quitter'][0](0)()" , { '__builtins__' : None }) user:~$ |
configobj,urllib,urllib2,setuptools等模块中都有os模块的内置:
1 2 3 4 5 6 7 8 9 | >>> import configobj >>> 'os' in configobj.__dict__ True >>> import urllib2 >>> 'os' in urllib2.__dict__ True >>> import setuptools >>> 'os' in setuptools.__dict__ True |
使用zipimport通过egg文件导入这些模块就可以使用os模块了:
1 | eval ( "[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'zipimporter'][0]('/path/to/configobj-5.0.5-py2.7.egg').load_module('configobj').os.getcwd()" , { '__builtins__' : None }) |
以上可以看出,eval()函数的漏洞还是很多的,如果只是用来做类型转换,可以使用ast.literal_eval 代替eval:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | >>> import ast >>> ast.literal_eval( '[1, 2, 3]' ) [ 1 , 2 , 3 ] >>> ast.literal_eval( 'abs(10)' ) Traceback (most recent call last): File "<pyshell#12>" , line 1 , in <module> ast.literal_eval( 'abs(10)' ) File "C:\Python27\lib\ast.py" , line 80 , in literal_eval return _convert(node_or_string) File "C:\Python27\lib\ast.py" , line 79 , in _convert raise ValueError( 'malformed string' ) ValueError: malformed string >>> eval ( 'abs(10)' ) 10 |
exec(str [, globals[, locals]])
execfile(filename [, globals[, locals]])
类似的,exec函数执行一个包含python代码的字符串,execfile则执行一个文件,后两个参数与eval类似。
1 2 | >>> a = [ 1 , 2 , 3 , 4 ] >>> exec ( 'for i in a: print i' ) |
1 2 | activate_this = '/path/to/env/bin/activate_this.py' execfile (activate_this, dict (__file__ = activate_this)) |
给eval或者exec函数传递字符串时,解析器首先会把字符串编译成字节码,为避免消耗资源,可以使用compile函数将字符串预编译:
compile(str, filename, kind)
str是要预编译的字符串,filename为字符串所在的文件,kind参数为single时代表一条语句,exec代表一组语句,eval代表一个表达式
1 2 3 4 5 6 7 8 9 10 | >>> s = 'for i in [1, 2, 3]: print i' >>> c = compile (s, ' ', ' exec ') >>> exec (c) 1 2 3 >>> s2 = '1+1' >>> c2 = compile (s2, ' ', ' eval ') >>> eval (c2) 2 |
eval执行exec类型的预编译字节码时,会返回None:
1 2 3 4 | >>> s2 = '1+1' >>> c2 = compile (s2, ' ', ' exec ') >>> print eval (c2) None |
以前看到过一个案例,使用eval转换不规则的json数据:
1 | >>> blog = "{url : 'www.example.com'}" |
上面url缺少引号,使用eval和json.loads()均会报错。
1 2 3 4 5 6 | >>> eval (blog) Traceback (most recent call last): File "<pyshell#27>" , line 1 , in <module> eval (blog) File "<string>" , line 1 , in <module> NameError: name 'url' is not defined |
使用eval时,eval会将url视为变量名并试图在globals和locals中寻找url的值,添加一下globals参数:
1 2 | >>> eval (blog, { 'url' : 'url' }) { 'url' : 'www.example.com' } |
所以,现在要做的是就是建立一个类dict的映射对象,其中value的值需要与key值相同,可以使用type函数来建立:
1 2 3 | >>> ValueFromKey = type ( 'ValueFromKey' , ( dict ,), dict (__getitem__ = lambda self ,k : k)) >>> eval (blog, ValueFromKey()) { 'url' : 'www.example.com' } |
在新建的type对象基类元祖中包含下dict,然后在对象的dict中定义一下__getitem__方法,使value值与key值一致即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探