Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VolgaCTF 2021 Qualifier - flask-admin #31

Open
aszx87410 opened this issue Mar 28, 2021 · 3 comments
Open

VolgaCTF 2021 Qualifier - flask-admin #31

aszx87410 opened this issue Mar 28, 2021 · 3 comments
Labels

Comments

@aszx87410
Copy link
Owner

aszx87410 commented Mar 28, 2021

flask-admin

Description

Incorrect usage of this library leads to serious consequences...

routes.py

from app import app, db
from flask import render_template, render_template_string, request, flash, redirect, url_for, send_from_directory, make_response, abort
import flask_admin as admin
from flask_admin import Admin, expose, base
from flask_admin.contrib.sqla import ModelView
from flask_login import current_user, login_user, logout_user
from app.models import User, Role
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, TextAreaField, validators, widgets,fields, SelectMultipleField
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
from app.decorators import admin_required, user_required
from werkzeug.urls import url_parse
import os

#-------------------------Admins-------------------------
class MyAdmin(admin.AdminIndexView):
    @expose('/')
    @admin_required
    def index(self):
        return super(MyAdmin, self).index()

    @expose('/user')
    @expose('/user/')
    @admin_required
    def user(self):
        return render_template_string('TODO, need create custom view')

admin = Admin(app, name='VolgaCTF', template_mode='bootstrap3', index_view=MyAdmin())
admin.add_view(ModelView(User, db.session))
#--------------------------------------------------------


#-------------------------Forms-------------------------
class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()],render_kw={"placeholder": "username"})
    password = PasswordField('Password', validators=[DataRequired()],render_kw={"placeholder": "password"})
    remember_me = BooleanField('Remember Me')
    submit = SubmitField('Sign In')

class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()], render_kw={"placeholder": "username"})
    email = StringField('Email', validators=[DataRequired(), validators.Length(1, 64), Email()],render_kw={"placeholder": "admin@admin.ru"})
    password = PasswordField('Password', validators=[DataRequired()],render_kw={"placeholder": "password"})
    password2 = PasswordField(
        'Repeat Password', validators=[DataRequired(), EqualTo('password')], render_kw={"placeholder": "password"})
    submit = SubmitField('Register')

    def validate_username(self, field):
        if User.query.filter_by(username=field.data).first():
            raise ValidationError('Username already in use.')

    def validate_email(self, field):
        if User.query.filter_by(email=field.data.lower()).first():
            raise ValidationError('Email already registered.')
#-------------------------------------------------------


@app.route('/')
def index():
    if current_user and current_user.is_authenticated and current_user.role.name == 'Administrator':
        return os.environ.get('Volga_flag') or 'Error, not found flag'
    return 'Hello, to get the flag, log in as admin'



@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user is None or not user.check_password(form.password.data):
            flash('Invalid username or password')
            return redirect(url_for('login'))
        else:
            # Keep the user info in the session using Flask-Login
            login_user(user, remember=form.remember_me.data)

        next_page = request.args.get('next')
        if not next_page or url_parse(next_page).netloc != '':
            next_page = url_for('index')
        return redirect(next_page)
    return render_template('login.html', title='Sign In', form=form)


def permission_check(permission):
    flag = False
    try:
        if current_user.can(permission):
            return True
        else:
            return False
    except AttributeError:
        return False
    return flag

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


@app.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = RegistrationForm()
    if form.validate_on_submit():
        user = User(username=form.username.data, email=form.email.data)
        user.set_password(form.password.data)
        db.session.add(user)
        db.session.commit()
        flash('Congratulations, you are now a registered user!')
        return redirect(url_for('login'))
    return render_template('register.html', title='Register', form=form)

Writeup

I have no idea how to solve this at first because I am not familiar with Python. But I want to solve this one so I go to check the documentation: https://flask-admin.readthedocs.io/en/latest/api/mod_base/#default-view

This part is strange because I haven't seen this usage in the documentation:

#-------------------------Admins-------------------------
class MyAdmin(admin.AdminIndexView):
    @expose('/')
    @admin_required
    def index(self):
        return super(MyAdmin, self).index()

    @expose('/user')
    @expose('/user/')
    @admin_required
    def user(self):
        return render_template_string('TODO, need create custom view')

admin = Admin(app, name='VolgaCTF', template_mode='bootstrap3', index_view=MyAdmin())
admin.add_view(ModelView(User, db.session))

I guess there are some endpoints which are not blocked so I tried /admin, /admin/user, /admin/user/ but all blocked.

I believe there must be something but I am lazy to reproduce the environment locally so I checked youtube video: https://www.youtube.com/watch?v=0cySORIhkCg&ab_channel=PrettyPrinted

And I found useful url /admi/user/new

http://172.105.84.156:5000/admin/user/new/

螢幕快照 2021-03-28 下午9 57 03

We can insert any user with admin role now! But what about password hash? how do I know what is the format?

The answer is: check youtube video again: https://www.youtube.com/watch?v=ysdShEL1HMM&ab_channel=PrettyPrinted

Found another useful url /admin/user/edit?id=1

螢幕快照 2021-03-28 下午9 58 03

I searched the keyword: pbkdf2:sha256:150000 and found this: https://www.cnblogs.com/jackadam/p/12196826.html

It seems pbkdf2:sha256:150000$ODedbYPS$4d1bd12adb1eb63f78e49873cbfc731e35af178cb9eb6b8b62c09dcf8db76670 is hello so I created an admin account with this password.

I logged in with the account just created and successfully got the flag.

@bhaviksec
Copy link

Quick query . When you mention /admin and /admin/user was blocked . did it asked you to login to http://172.105.84.156:5000/?
Since there is admin_required decorator I believe it checked if current user is admin or not .?

As the method @app.route('/') checks for admin user.
def index():
if current_user and current_user.is_authenticated and current_user.role.name == 'Administrator':
return os.environ.get('Volga_flag') or 'Error, not found flag'
return 'Hello, to get the flag, log in as admin'

@aszx87410
Copy link
Owner Author

I solved it a while ago so I am not sure, but I think it shows 403 forbidden or other error page, told me that I have no permission to view this page. It won't ask me to login because I already logged in.

@bhaviksec
Copy link

bhaviksec commented Sep 1, 2021

Ok Thanks. Since this method @app.route('/') checks for user role I thought the first thing on site would be to login

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants