[SCTF 2021]loginme go语言ssti漏洞

今天做个新颖的题,go中的ssti问题。进来点击访问/admin/index?id=1发现空白,只有admin能看,看看源码main.go。

点击查看代码
package main

import (
	"html/template"
	"loginme/middleware"
	"loginme/route"
	"loginme/templates"

	"github.com/gin-gonic/gin"
)

func main() {
	gin.SetMode(gin.ReleaseMode)
	r := gin.Default()
	templ := template.Must(template.New("").ParseFS(templates.Templates, "*.tmpl"))
	r.SetHTMLTemplate(templ)

	r.Use(gin.Logger())
	r.Use(gin.Recovery())
	authorized := r.Group("/admin")
	authorized.Use(middleware.LocalRequired())
	{
		authorized.GET("/index", route.Login)
	}

	r.GET("/", route.Index)
	r.Run(":9999")
}

authorized.Use(middleware.LocalRequired())用到了middleware,去看看middleware.go。
点击查看代码
package middleware

import (
	"github.com/gin-gonic/gin"
)

func LocalRequired() gin.HandlerFunc {
	return func(c *gin.Context) {
		if c.GetHeader("x-forwarded-for") != "" || c.GetHeader("x-client-ip") != "" {
			c.AbortWithStatus(403)
			return
		}
		ip := c.ClientIP()
		if ip == "127.0.0.1" {
			c.Next()
		} else {
			c.AbortWithStatus(401)
		}
	}
}

意思就是构造头部为127.0.0.1本地访问,不能用那个用x-forwarded-for与x-client-ip,那就一个一个试,发现x-real-ip可使用。

看看其他源码route.go,struct.go

点击查看代码
package route

import (
	_ "embed"
	"fmt"
	"html/template"
	"loginme/structs"
	"loginme/templates"
	"strconv"

	"github.com/gin-gonic/gin"
)

func Index(c *gin.Context) {
	c.HTML(200, "index.tmpl", gin.H{
		"title": "Try Loginme",
	})
}

func Login(c *gin.Context) {
	idString, flag := c.GetQuery("id")
	if !flag {
		idString = "1"
	}
	id, err := strconv.Atoi(idString)
	if err != nil {
		id = 1
	}
	TargetUser := structs.Admin
	for _, user := range structs.Users {
		if user.Id == id {
			TargetUser = user
		}
	}

	age := TargetUser.Age
	if age == "" {
		age, flag = c.GetQuery("age")
		if !flag {
			age = "forever 18 (Tell me the age)"
		}
	}

	if err != nil {
		c.AbortWithError(500, err)
	}

	html := fmt.Sprintf(templates.AdminIndexTemplateHtml, age)
	if err != nil {
		c.AbortWithError(500, err)
	}

	tmpl, err := template.New("admin_index").Parse(html)
	if err != nil {
		c.AbortWithError(500, err)
	}

	tmpl.Execute(c.Writer, TargetUser)
}



package structs

type UserInfo struct {
	Id       int
	Username string
	Age      string
	Password string
}

var Users = []UserInfo{
	{
		Id:       1,
		Username: "Grandpa Lu",
		Age:      "22",
		Password: "hack you!",
	},
	{
		Id:       2,
		Username: "Longlone",
		Age:      "??",
		Password: "i don't know",
	},
	{
		Id:       3,
		Username: "Teacher Ma",
		Age:      "20",
		Password: "guess",
	},
}

var Admin = UserInfo{
	Id:       0,
	Username: "Admin",
	Age:      "",
	Password: "flag{}",
}


这里涉及到了go ssti的一些知识,看一个例子:
点击查看代码
package main

import (
    "net/http"
    "text/template"
)

type User struct {
    ID       int
    Name     string
    Email    string
    Password string
}

func StringTpl2Exam(w http.ResponseWriter, r *http.Request) {
    user := &User{1,"John", "test@example.com", "test123"}
    r.ParseForm()
    tpl := `<h1>Hi, {{ .Name }}</h1><br>Your Email is {{ .Email }}`
    data := map[string]string{
        "Name":  user.Name,
        "Email": user.Email,
    }
    html := template.Must(template.New("login").Parse(tpl))
    html.Execute(w, data)
}

func main() {
    server := http.Server{
        Addr: "127.0.0.1:8888",
    }
    http.HandleFunc("/string", StringTpl2Exam)
    server.ListenAndServe()
}

模板内容 <h1>Hi, {{ .Name }}</h1><br>Your Email is {{ .Email }}
期待输出 <h1>Hi, John</h1><br>Your Email is test@example.com
go的模版渲染使用的是{{}},可以看到当传入参数可控时,就会经过动态内容生成不同的内容。 Go 提供了两个模板包。一个是 text/template,另一个是html/template。text/template对 XSS 或任何类型的 HTML 编码都没有保护,因此该模板并不适合构建 Web 应用程序,而html/template与text/template基本相同,但增加了HTML编码等安全保护,更加适用于构建web应用程序。但是两个包都能够构造正常的payload来进行ssti,只是在heml/template中传入的script和js都会被转义,很好地防范了xss,但text/template也提供了内置函数html来转义特殊字符,除此之外还有js,也存在template.HTMLEscapeString等转义函数。 列出几个常用的ssti payload:
点击查看代码
{{.}} 表示当前对象,如user对象

{{.FieldName}} 表示对象的某个字段 如{{.Name}}user对象的Name字段

{{range …}}{{end}} goforrange语法类似,循环

{{with …}}{{end}} 当前对象的值,上下文

{{if …}}{{else}}{{end}} go中的if-else语法类似,条件选择

{{xxx | xxx}} 左边的输出作为右边的输入

{{template "navbar"}} 引入子模版
知道相关知识后看这道题,在结构体中表明了admin的Password字段是flag,那么将其输出即可。

总结:

  1. go语言中的ssti
  2. text/template与html/template
posted @   jockerliu  阅读(99)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示