Go SSTI初探

前言

这几天学习了Go,体验还行,感觉没有网上说的那么不堪(可能是我开发太菜了体会不到emmm),就来了解一下Go方面的漏洞。于是,这篇ssti初探就成了我入门Go安全的第一步。

声明:Go SSTI漏洞成因与模板语法与jinja2都大差不差,所以这方面的介绍就可以省了.

安全问题

识别方法:与其他SSTI识别方法不同,运算符在{{}}中是非法的,因此需要使用其他的payload,如{{.}}占位符,如果存在SSTI,那么应当无回显。当然,点替换为任意字符串也可以。

信息泄露

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

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

type User struct {
	Id     int
	Name   string
	Passwd string
}

func StringTplExam(w http.ResponseWriter, r *http.Request) {
	user := &User{1, "tyskill", "tyskill"}
	r.ParseForm()
	arg := strings.Join(r.PostForm["name"], "")
	tpl1 := fmt.Sprintf(`<h1>Hi, ` + arg + `</h1> Your name is {{ .Name }}`)
	html, err := template.New("login").Parse(tpl1)
	html = template.Must(html, err)
	html.Execute(w, user)
}

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

结构体是Go中一个非常重要的类型,Go通过结构体来类比一个对象,因此他的字段就是一个对象的属性。通常Json的编码和解析都需要通过结构体来实现,加之模板渲染支持传入一个结构体的实例来渲染它的字段,这就造成了信息泄漏的可能。如代码所示,输入可控且直接拼接进模板,仿造{{ .Name }}的结构构造{{.Passwd}}就能获得Passwd字段值。

防御方法:拒绝拼接

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func StringTpl2Exam(w http.ResponseWriter, r *http.Request) {
	user := &User{1, "tyskill", "tyskill"}
	r.ParseForm()
	arg := strings.Join(r.PostForm["name"], "")
	tpl := `<h1>Hi, {{ .arg }}</h1><br>Your name is {{ .Name }}`
	data := map[string]string{
		"arg":  arg,
		"Name": user.Name,
	}
	html := template.Must(template.New("login").Parse(tpl))
	html.Execute(w, data)
}

XSS

借助Go模板提供的字符串打印功能,可以直接输出XSS语句,上面修改的的防御方法也无法阻挡弹窗的脚步

{{"<script>alert(/xss/)</script>"}}
{{print "<script>alert(/xss/)</script>"}}

防御方法

1、Go模板包text/template提供了内置函数html来转义特殊字符,除此之外还提供了js函数转义js代码。

{{html "<script>alert(/xss/)</script>"}}
{{js "js代码"}}

2、text/template在模板处理阶段还定义template.HTMLEscapeString等转义函数

3、使用另一个模板包html/template,自带转义效果

命令执行&任意文件读取

这个就比较空中楼阁了,通过模板语法可知可以像{{ .Name }}一样调用对象方法,模板内部并不存在可以RCE的函数,所以除非有人为渲染对象定义了RCE或文件读取的方法,不然这个问题是不存在的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func (u *User) System(cmd string, arg ...string) string {
	out, _ := exec.Command(cmd, arg...).CombinedOutput()
	return string(out)
}

func (u *User) FileRead(File string) string {
	data, err := ioutil.ReadFile(File)
	if err != nil {
		fmt.Print("File read error")
	}
	return string(data)
}

如果定义了就可以通过{{.System "whoami"}}{{.FileRead "filepath"}}执行

防御方法:不会真有人给对象定义这么危险的方法吧😅

总结

Go SSTI杀伤力没有jinja2那么大,甚至在黑盒情况下只有XSS能被测出来,就算测出来了说不定也会被拒收🐶

参考

http://www.systonsoft.com/article/290811.html

https://golang.org/pkg/text/template/

https://www.onsecurity.io/blog/go-ssti-method-research/