攻防世界 web高手进阶区 9分题 Web_python_flask_sql_injection

前言

继续ctf的旅程
开始攻防世界web高手进阶区的9分题
本文是Web_python_flask_sql_injection的writeup

解题过程

在这里插入图片描述
给了一堆源码
在这里插入图片描述
这是 Flask 框架的结构
正好根据题目这应该是个SSTI和sql注入的题

源码分析

看看源码先
主要是找跟数据库相关的可能存在注入的地方

1、error.py

from flask import render_template
from app import app, db_session


@app.errorhandler(404)
def not_found_error(error):
    return render_template('404.html'), 404


@app.errorhandler(500)
def internal_error(error):
    return render_template('500.html'), 500

这里注册了两个错误处理函数
在整个 app 发生 HTTP 404 或者 HTTP 500 错误时 , 返回特定的页面

在 Flask 框架中 , 使用 @app.errorhandler() 装饰器来注册错误处理函数
该装饰器可以传入一个 HTTP 错误状态码 , 或者是特定的异常类

2、forms.py

import re
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
from app import mysql
from flask_login import current_user


class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    remember_me = BooleanField('Remember Me')
    submit = SubmitField('Sign In')


class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])
    password2 = PasswordField(
        'Repeat Password', validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Register')

    def validate_username(self, username):
        if re.match("^[a-zA-Z0-9_]+$", username.data) == None:
            raise ValidationError('username has invalid charactor!')
        user = mysql.One("user", {
   "username": "'%s'" % username.data}, ["id"])
        if user != 0:
            raise ValidationError('Please use a different username.')

    def validate_email(self, email):
        user = mysql.One("user", {
   "email":  "'%s'" % email.data}, ["id"])
        if user != 0:
            raise ValidationError('Please use a different email address.')


class ResetPasswordRequestForm(FlaskForm):
    email = StringField('Email', validators=[DataRequired(), Email()])
    submit = SubmitField('Request Password Reset')


class ResetPasswordForm(FlaskForm):
    password = PasswordField('Password', validators=[DataRequired()])
    password2 = PasswordField(
        'Repeat Password', validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Request Password Reset')


class EditProfileForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    note = StringField('About me', validators=[])
    submit = SubmitField('Submit')

    def __init__(self, original_username, *args, **kwargs):
        super(EditProfileForm, self).__init__(*args, **kwargs)
        self.original_username = original_username

    def validate_username(self, username):
        if re.match("^[a-zA-Z0-9_]+$", username.data) == None:
            raise ValidationError('username has invalid charactor!')

        if username.data == current_user.username:
            pass
        else:
            user = mysql.One(
                "user", {
   "username": "'%s'" % username.data}, ["id"])
            if user != 0:
                raise ValidationError('Please use a different username.')

    def validate_note(self, note):
        if re.match("^[a-zA-Z0-9_\'\(\) \.\_\*\`\-\@\=\+\>\<]*$", note.data) == None:
            raise ValidationError("Don't input invalid charactors!")


class PostForm(FlaskForm):
    post = StringField('Say something', validators=[DataRequired()])
    submit = SubmitField('Submit')

这里用了Flask-WTF

  • StringField : 表示字符串文本框
  • validators : 指定提交表单的验证顺序
  • DataRequired : 验证数据是否存在 , 不能为空
  • Email() : 验证数据是否符合最基本的邮件格式
  • PasswordField : 表示密码文本框 , 输入的内容不会直接以明文显示
  • EqualTo : 验证两个字段的值是否相等

RegistrationForm 类中定义了两个函数 , 分别用于验证用户名和邮箱是否可用

  • 先判断输入的用户名是否仅由字母数字下划线构成( 即是否存在特殊字符 )
    若正确则调用 Mysql.One 函数 , 判断该用户是否存在 , 若不存在则会抛出异常

  • 对用户输入的 Email 地址调用 mysql.One 函数 , 判断该邮箱地址是否已被注册
    这个验证过程与用户名的验证形成了鲜明的对比 , 很容易发现这里缺少了对邮箱地址的验证 , 只要用户输入的邮箱地址满足最基本的邮箱格式 , 该地址就会被带入数据库中查询

编辑个人档案时调用了数据库 , 但是输入字段 username 的值已经通过了正则过滤 , 因此应该不存在利用点

发送帖子的过程没有直接调用数据库

3、init.py

from flask import Flask
from flask_login import LoginManager
from flask_bootstrap import Bootstrap
from flask_moment import Moment

from config import Config
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base, DeferredReflection
from others import Mysql_Operate
from Mysessions import FileSystemSessionInterface
app = Flask(__name__)
app.config.from_object(Config)

engine = create_engine(
    app.config['SQLALCHEMY_DATABASE_URI'], convert_unicode=True)
db_session = scoped_session(sessionmaker(
    autocommit=False, autoflush=False, bind=engine))
Base = declarative_base(cls=DeferredReflection)
Base.query = db_session.query_property()
mysql = Mysql_Operate(Base, engine, db_session)

login = LoginManager(app)
login.login_view = 'login'
bootstrap = Bootstrap(app)
moment = Moment(app)

app.session_interface = FileSystemSessionInterface(
    app.config['SESSION_FILE_DIR'], app.config['SESSION_FILE_THRESHOLD'],
    app.config['SESSION_FILE_MODE'])

from app import routes, models, errors

这个文件主要用于控制包的导入和各类初始化行为

  • 框架和函数的导入
  • 初始化 Flask 应用
  • 初始化数据库连接

4、models.py

from datetime import datetime
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from app import Base, login, mysql
from sqlalchemy import Column, Integer, String, Date, ForeignKey
from sqlalchemy.orm import relationship, backref


class Followers(Base):
    __tablename__ = 'followers'
    follower_id = Column('follower_id', Integer, ForeignKey('user.id'))
    followed_id = Column('followed_id', Integer, ForeignKey('user.id'))


class User(UserMixin, Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True)
    username = Column(String(64), index=True, unique=True)
    email = Column(String(120), index=True, unique=True)
    password_hash = Column(String(128))
    posts = relationship('Post', backref='author', lazy='dynamic')
    note = Column(String(140))
    last_seen = Column(Date, default=datetime.utcnow)
    followed = relationship(
        'User', secondary=Followers,
        primaryjoin=(Followers.follower_id == id),
        secondaryjoin=(Followers.followed_id == id),
        backref=backref('Followers', lazy='dynamic'), lazy='dynamic')

    def __repr__(self):
        return '<User {}>'.format(self.username)

    def set_password
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值