注册账号登录,打开f12我们能够看到github的提示,我们把源码下载下来进行分析。
查看初始化模块
from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login = LoginManager(app)
from app import routes, models
查看routes文件
修改password模块
@app.route('/change', methods = ['GET', 'POST'])
def change():
if not current_user.is_authenticated:
return redirect(url_for('login'))
form = NewpasswordForm()
if request.method == 'POST':
name = strlower(session['name'])
user = User.query.filter_by(username=name).first()
user.set_password(form.newpassword.data)
db.session.commit()
flash('change successful')
return redirect(url_for('index'))
return render_template('change.html', title = 'change', form = form)
register登记注册模块
@app.route('/register', methods = ['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = RegisterForm()
if request.method == 'POST':
name = strlower(form.username.data)
if session.get('image').lower() != form.verify_code.data.lower():
flash('Wrong verify code.')
return render_template('register.html', title = 'register', form=form)
if User.query.filter_by(username = name).first():
flash('The username has been registered')
return redirect(url_for('register'))
user = User(username=name)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('register successful')
return redirect(url_for('login'))
return render_template('register.html', title = 'register', form = form)
查看渲染的index页面
{% include('header.html') %}
{% if current_user.is_authenticated %}
<h1 class="nav">Hello {{ session['name'] }}</h1>
{% endif %}
{% if current_user.is_authenticated and session['name'] == 'admin' %}
<h1 class="nav">hctf{xxxxxxxxx}</h1>
{% endif %}
<!-- you are not admin -->
<h1 class="nav">Welcome to hctf</h1>
{% include('footer.html') %}
发现判断逻辑: {% if current_user.is_authenticated and session['name'] == 'admin' %}
证明要使用admin账号才能登陆。
config.py
import os
class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or 'ckj123'
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:adsl1234@db:3306/test'
SQLALCHEMY_TRACK_MODIFICATIONS = True
flask的session是存储在客户端cookie中的,而且flask仅仅对数据进行了签名。众所周知的是,签名的作用是防篡改,而无法防止被读取。而flask并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的,这就可能造成一些安全问题。
利用脚本我们将session解密,之后修改用户名为admin,然后再加密,burp截包替换session即可。
{'_fresh': True, '_id': b'121de14bca66edf6cc98e254ab460d68f9122c75e64747a997410a84049d9295b53192aebf5c2b93641e5c58cc1596ed3850da7a17a5f3f6415ac0743afe3dc4', 'csrf_token': b'd2495789467d55d9e38c2ffd63e9c578ee1b267a', 'image': b'BUXE', 'name': 'admin', 'user_id': '10'}
unicode欺骗
无论是在register,changepassword模块他们都加了strlower()进行小写转换,右键转到函数的实现。
def strlower(username):
username = nodeprep.prepare(username)
return username
使用到nodeprep.prepare函数,该函数专函大小写字符如下:
ᴬᴰᴹᴵᴺ -> ADMIN -> admin
我们注册ᴬDMIN用户,在registr中这个函数没被使用不被转义,在login进行第一次的编码转换,在changepassword又进行第二次,此时我们用户名就由ᴬDMIN变为了admin,修改了admin的password。
转换形式如下:
ᴬᴰᴹᴵᴺ -> ADMIN -> admin
条件竞争
这个在刚入学时候极客大挑战已经碰见过了,我们利用burpsuit的intruder模块就能利用,要设置两个线程同时进行,一个是不行的。