这几天学习了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)
}
|
借助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/