buuctf_python审计_[HCTF 2018]admin

2022-07-27,

题目:[HCTF 2018]admin 1

出处:https://buuoj.cn/challenges

知识点

1.发现源码泄露的能力
2.python审计
3.flask框架知识
4.flask session加解密方法

题面
解题步骤

法二
1.本题的/change页面的前端代码中,有一句 <!-- https://github.com/woadsl1234/hctf_flask/ -->,这说明这题的源代码已经在github上泄露

2.先看看https://github.com/woadsl1234/hctf_flask/config.py看到如下内容:

3.由此猜测可能可以直接访问数据库,我们来测试一下3306是否存活telnet一下:

失败了。

4.既然源码泄露了,config.py里面的信息也不能帮我登录到数据库,我也不想对着wp照抄,我决定来审计一下源码

python和php不同所有传输的参数都通过route来接收,我们下载源码利用search and replace这个软件查询route(

我们可以看见,整个程序用户可以控制的包其实也就这几个,而这次代码泄漏的提示这么难找非要放在/change里面基本应该是在暗示,问题出在/change这里,我们打开这个route.py文件读一下源代码,避免浪费时间,不太重要的源码的功能我直接写在程序的注释里:

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')
#处理xxxxx/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('/')
#处理xxxxx/的请求
@app.route('/index')
#处理xxxxx/index的请求
def index():
    return render_template('index.html', title = 'hctf')

@app.route('/register', methods = ['GET', 'POST'])
#处理xxxx/register?xxx=xxx
def register():

    if current_user.is_authenticated:
        return redirect(url_for('index'))

    form = RegisterForm()
    if request.method == 'POST':
    	#读取form-data的数据username为name
        name = strlower(form.username.data)
        #通过session检验验证码是否正确(此处验证码似乎可以绕过)
        if session.get('image').lower() != form.verify_code.data.lower():
            flash('Wrong verify code.')
            return render_template('register.html', title = 'register', form=form)
        #检验用户名是否被注册过,若绕过这一步可能可以直接重置admin的密码,但好像不行
        if User.query.filter_by(username = name).first():
            flash('The username has been registered')
            return redirect(url_for('register'))
        #直接将密码赋予从username读取到的user
        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']
        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':
    	#session['name']是login时候赋值的其值为当前用户的用户名,但session分明是一串乱码,查阅资料之后了解到这涉及到了flask session的一个漏洞,flask将session存在客户端且只通过一个时间戳和secret_key对session中用户名进行了加密,仅需要session本身和secret_key就可以对破解出name。
        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

目前我们发现了这题最容易得到admin权限的方法是利用/change修改admin的密码,这可能需要利用漏洞flask session的漏洞(实际该漏洞可利用还是由于源码泄漏):flask将session存在客户端且只通过一个时间戳和secret_key对session中用户名进行了加密,仅需要session本身和secret_key就可以对破解出name。

我们来读取一下secret_key

找到了,secret_keyckj123
开始使用白嫖来的解密脚本:

#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode

def decryption(payload):
    payload, sig = payload.rsplit(b'.', 1)
    payload, timestamp = payload.rsplit(b'.', 1)

    decompress = False
    if payload.startswith(b'.'):
        payload = payload[1:]
        decompress = True

    try:
        payload = base64_decode(payload)
    except Exception as e:
        raise Exception('Could not base64 decode the payload because of '
                         'an exception')

    if decompress:
        try:
            payload = zlib.decompress(payload)
        except Exception as e:
            raise Exception('Could not zlib decompress the payload before '
                             'decoding the payload')

    return session_json_serializer.loads(payload)

if __name__ == '__main__':
    print(decryption(sys.argv[1].encode()))   

得到解密后的session:

然后在解密后的session中将asdf改成admin,再加密:

通过burp将加密后的session修改到xxxx/change的包里

成功得到flag。

总结:这道题做完半条命没了,明天见短短不宜晚睡,祝看文章的大佬们做一个苏维埃梦。

参考
https://www.cnblogs.com/apossin/p/10083937.html
https://blog.csdn.net/rfrder/article/details/109188719
https://github.com/noraj/flask-session-cookie-manager

本文地址:https://blog.csdn.net/ratear/article/details/109788823

《buuctf_python审计_[HCTF 2018]admin.doc》

下载本文的Word格式文档,以方便收藏与打印。